about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/NetworkManagerUtils.c65
-rw-r--r--src/NetworkManagerUtils.h69
-rw-r--r--src/devices/adsl/meson.build15
-rw-r--r--src/devices/adsl/nm-atm-manager.c18
-rw-r--r--src/devices/adsl/nm-device-adsl.c18
-rw-r--r--src/devices/adsl/nm-device-adsl.h18
-rw-r--r--src/devices/bluetooth/meson.build36
-rw-r--r--src/devices/bluetooth/nm-bluez-common.h25
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c1320
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h84
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c2881
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.h39
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c456
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.h57
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.c355
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.h40
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c928
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h59
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c596
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h40
-rw-r--r--src/devices/bluetooth/nm-bt-error.c18
-rw-r--r--src/devices/bluetooth/nm-bt-error.h18
-rw-r--r--src/devices/bluetooth/nm-device-bt.c898
-rw-r--r--src/devices/bluetooth/nm-device-bt.h52
-rw-r--r--src/devices/bluetooth/tests/nm-bt-test.c220
-rw-r--r--src/devices/nm-acd-manager.c16
-rw-r--r--src/devices/nm-acd-manager.h16
-rw-r--r--src/devices/nm-device-6lowpan.c36
-rw-r--r--src/devices/nm-device-6lowpan.h20
-rw-r--r--src/devices/nm-device-bond.c64
-rw-r--r--src/devices/nm-device-bond.h20
-rw-r--r--src/devices/nm-device-bridge.c152
-rw-r--r--src/devices/nm-device-bridge.h23
-rw-r--r--src/devices/nm-device-dummy.c31
-rw-r--r--src/devices/nm-device-dummy.h20
-rw-r--r--src/devices/nm-device-ethernet-utils.c18
-rw-r--r--src/devices/nm-device-ethernet-utils.h18
-rw-r--r--src/devices/nm-device-ethernet.c80
-rw-r--r--src/devices/nm-device-ethernet.h18
-rw-r--r--src/devices/nm-device-factory.c40
-rw-r--r--src/devices/nm-device-factory.h53
-rw-r--r--src/devices/nm-device-generic.c20
-rw-r--r--src/devices/nm-device-generic.h20
-rw-r--r--src/devices/nm-device-infiniband.c54
-rw-r--r--src/devices/nm-device-infiniband.h20
-rw-r--r--src/devices/nm-device-ip-tunnel.c37
-rw-r--r--src/devices/nm-device-ip-tunnel.h20
-rw-r--r--src/devices/nm-device-logging.h18
-rw-r--r--src/devices/nm-device-macsec.c26
-rw-r--r--src/devices/nm-device-macsec.h20
-rw-r--r--src/devices/nm-device-macvlan.c37
-rw-r--r--src/devices/nm-device-macvlan.h20
-rw-r--r--src/devices/nm-device-ppp.c14
-rw-r--r--src/devices/nm-device-ppp.h12
-rw-r--r--src/devices/nm-device-private.h24
-rw-r--r--src/devices/nm-device-tun.c41
-rw-r--r--src/devices/nm-device-tun.h20
-rw-r--r--src/devices/nm-device-veth.c20
-rw-r--r--src/devices/nm-device-veth.h20
-rw-r--r--src/devices/nm-device-vlan.c34
-rw-r--r--src/devices/nm-device-vlan.h20
-rw-r--r--src/devices/nm-device-vxlan.c37
-rw-r--r--src/devices/nm-device-vxlan.h20
-rw-r--r--src/devices/nm-device-wireguard.c64
-rw-r--r--src/devices/nm-device-wireguard.h18
-rw-r--r--src/devices/nm-device-wpan.c38
-rw-r--r--src/devices/nm-device-wpan.h20
-rw-r--r--src/devices/nm-device.c1056
-rw-r--r--src/devices/nm-device.h60
-rw-r--r--src/devices/nm-lldp-listener.c18
-rw-r--r--src/devices/nm-lldp-listener.h18
-rw-r--r--src/devices/ovs/meson.build10
-rw-r--r--src/devices/ovs/nm-device-ovs-bridge.c20
-rw-r--r--src/devices/ovs/nm-device-ovs-bridge.h20
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.c34
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.h20
-rw-r--r--src/devices/ovs/nm-device-ovs-port.c20
-rw-r--r--src/devices/ovs/nm-device-ovs-port.h20
-rw-r--r--src/devices/ovs/nm-ovs-factory.c24
-rw-r--r--src/devices/ovs/nm-ovsdb.c18
-rw-r--r--src/devices/ovs/nm-ovsdb.h20
-rw-r--r--src/devices/team/meson.build10
-rw-r--r--src/devices/team/nm-device-team.c223
-rw-r--r--src/devices/team/nm-device-team.h18
-rw-r--r--src/devices/team/nm-team-factory.c20
-rw-r--r--src/devices/tests/meson.build4
-rw-r--r--src/devices/tests/test-acd.c18
-rw-r--r--src/devices/tests/test-lldp.c18
-rw-r--r--src/devices/wifi/meson.build30
-rw-r--r--src/devices/wifi/nm-device-iwd.c44
-rw-r--r--src/devices/wifi/nm-device-iwd.h18
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c38
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.h25
-rw-r--r--src/devices/wifi/nm-device-wifi-p2p.c96
-rw-r--r--src/devices/wifi/nm-device-wifi-p2p.h21
-rw-r--r--src/devices/wifi/nm-device-wifi.c218
-rw-r--r--src/devices/wifi/nm-device-wifi.h18
-rw-r--r--src/devices/wifi/nm-iwd-manager.c20
-rw-r--r--src/devices/wifi/nm-iwd-manager.h18
-rw-r--r--src/devices/wifi/nm-wifi-ap.c123
-rw-r--r--src/devices/wifi/nm-wifi-ap.h19
-rw-r--r--src/devices/wifi/nm-wifi-common.c18
-rw-r--r--src/devices/wifi/nm-wifi-common.h18
-rw-r--r--src/devices/wifi/nm-wifi-factory.c18
-rw-r--r--src/devices/wifi/nm-wifi-p2p-peer.c19
-rw-r--r--src/devices/wifi/nm-wifi-p2p-peer.h19
-rw-r--r--src/devices/wifi/nm-wifi-utils.c254
-rw-r--r--src/devices/wifi/nm-wifi-utils.h18
-rw-r--r--src/devices/wifi/tests/meson.build14
-rw-r--r--src/devices/wifi/tests/test-devices-wifi.c16
-rw-r--r--src/devices/wwan/libnm-wwan.ver6
-rw-r--r--src/devices/wwan/meson.build42
-rw-r--r--src/devices/wwan/nm-device-modem.c104
-rw-r--r--src/devices/wwan/nm-device-modem.h18
-rw-r--r--src/devices/wwan/nm-modem-broadband.c193
-rw-r--r--src/devices/wwan/nm-modem-broadband.h18
-rw-r--r--src/devices/wwan/nm-modem-manager.c32
-rw-r--r--src/devices/wwan/nm-modem-manager.h21
-rw-r--r--src/devices/wwan/nm-modem-ofono.c26
-rw-r--r--src/devices/wwan/nm-modem-ofono.h18
-rw-r--r--src/devices/wwan/nm-modem.c92
-rw-r--r--src/devices/wwan/nm-modem.h32
-rw-r--r--src/devices/wwan/nm-service-providers.c459
-rw-r--r--src/devices/wwan/nm-service-providers.h24
-rw-r--r--src/devices/wwan/nm-wwan-factory.c32
-rw-r--r--src/devices/wwan/tests/test-service-providers.c124
-rw-r--r--src/devices/wwan/tests/test-service-providers.xml73
-rw-r--r--src/dhcp/meson.build6
-rw-r--r--src/dhcp/nm-dhcp-client-logging.h18
-rw-r--r--src/dhcp/nm-dhcp-client.c120
-rw-r--r--src/dhcp/nm-dhcp-client.h35
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.c74
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.h17
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c57
-rw-r--r--src/dhcp/nm-dhcp-dhcpcanon.c20
-rw-r--r--src/dhcp/nm-dhcp-dhcpcd.c21
-rw-r--r--src/dhcp/nm-dhcp-helper-api.h21
-rw-r--r--src/dhcp/nm-dhcp-helper.c18
-rw-r--r--src/dhcp/nm-dhcp-listener.c22
-rw-r--r--src/dhcp/nm-dhcp-listener.h18
-rw-r--r--src/dhcp/nm-dhcp-manager.c107
-rw-r--r--src/dhcp/nm-dhcp-manager.h24
-rw-r--r--src/dhcp/nm-dhcp-nettools.c378
-rw-r--r--src/dhcp/nm-dhcp-options.c19
-rw-r--r--src/dhcp/nm-dhcp-options.h18
-rw-r--r--src/dhcp/nm-dhcp-systemd.c225
-rw-r--r--src/dhcp/nm-dhcp-utils.c70
-rw-r--r--src/dhcp/nm-dhcp-utils.h24
-rw-r--r--src/dhcp/tests/meson.build3
-rw-r--r--src/dhcp/tests/test-dhcp-dhclient.c57
-rw-r--r--src/dhcp/tests/test-dhcp-utils.c17
-rw-r--r--src/dns/nm-dns-dnsmasq.c1094
-rw-r--r--src/dns/nm-dns-dnsmasq.h16
-rw-r--r--src/dns/nm-dns-manager.c106
-rw-r--r--src/dns/nm-dns-manager.h19
-rw-r--r--src/dns/nm-dns-plugin.c243
-rw-r--r--src/dns/nm-dns-plugin.h74
-rw-r--r--src/dns/nm-dns-systemd-resolved.c39
-rw-r--r--src/dns/nm-dns-systemd-resolved.h16
-rw-r--r--src/dns/nm-dns-unbound.c50
-rw-r--r--src/dns/nm-dns-unbound.h17
-rw-r--r--src/dnsmasq/nm-dnsmasq-manager.c18
-rw-r--r--src/dnsmasq/nm-dnsmasq-manager.h18
-rw-r--r--src/dnsmasq/nm-dnsmasq-utils.c18
-rw-r--r--src/dnsmasq/nm-dnsmasq-utils.h18
-rw-r--r--src/dnsmasq/tests/meson.build3
-rw-r--r--src/dnsmasq/tests/test-dnsmasq-utils.c16
-rw-r--r--src/initrd/meson.build21
-rw-r--r--src/initrd/nm-initrd-generator.c26
-rw-r--r--src/initrd/nm-initrd-generator.h21
-rw-r--r--src/initrd/nmi-cmdline-reader.c30
-rw-r--r--src/initrd/nmi-dt-reader.c370
-rw-r--r--src/initrd/nmi-ibft-reader.c25
-rw-r--r--src/initrd/tests/meson.build12
-rw-r--r--src/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpathbin0 -> 108 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-requestbin0 -> 273 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-responsebin0 -> 300 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpathbin0 -> 37 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip1
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip2
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-namebin0 -> 9 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-namebin0 -> 15 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip2
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/namebin0 -> 7 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ipbin0 -> 4 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-pathbin0 -> 1 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip2
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-filebin0 -> 11 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-optionsbin0 -> 1 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_typebin0 -> 8 bytes
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address1
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address1
-rw-r--r--src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/namebin0 -> 9 bytes
-rw-r--r--src/initrd/tests/test-cmdline-reader.c21
-rw-r--r--src/initrd/tests/test-dt-reader.c132
-rw-r--r--src/initrd/tests/test-ibft-reader.c21
-rw-r--r--src/main-utils.c18
-rw-r--r--src/main-utils.h18
-rw-r--r--src/main.c37
-rw-r--r--src/meson.build125
-rw-r--r--src/ndisc/nm-fake-ndisc.c18
-rw-r--r--src/ndisc/nm-fake-ndisc.h18
-rw-r--r--src/ndisc/nm-lndp-ndisc.c18
-rw-r--r--src/ndisc/nm-lndp-ndisc.h18
-rw-r--r--src/ndisc/nm-ndisc-private.h20
-rw-r--r--src/ndisc/nm-ndisc.c18
-rw-r--r--src/ndisc/nm-ndisc.h18
-rw-r--r--src/ndisc/tests/meson.build6
-rw-r--r--src/ndisc/tests/test-ndisc-fake.c18
-rw-r--r--src/ndisc/tests/test-ndisc-linux.c18
-rw-r--r--src/nm-act-request.c18
-rw-r--r--src/nm-act-request.h20
-rw-r--r--src/nm-active-connection.c20
-rw-r--r--src/nm-active-connection.h18
-rw-r--r--src/nm-audit-manager.c20
-rw-r--r--src/nm-audit-manager.h20
-rw-r--r--src/nm-auth-manager.c106
-rw-r--r--src/nm-auth-manager.h21
-rw-r--r--src/nm-auth-subject.c18
-rw-r--r--src/nm-auth-subject.h18
-rw-r--r--src/nm-auth-utils.c18
-rw-r--r--src/nm-auth-utils.h18
-rw-r--r--src/nm-checkpoint-manager.c20
-rw-r--r--src/nm-checkpoint-manager.h18
-rw-r--r--src/nm-checkpoint.c18
-rw-r--r--src/nm-checkpoint.h18
-rw-r--r--src/nm-config-data.c110
-rw-r--r--src/nm-config-data.h71
-rw-r--r--src/nm-config.c77
-rw-r--r--src/nm-config.h21
-rw-r--r--src/nm-connectivity.c63
-rw-r--r--src/nm-connectivity.h18
-rw-r--r--src/nm-core-utils.c240
-rw-r--r--src/nm-core-utils.h33
-rw-r--r--src/nm-dbus-manager.c100
-rw-r--r--src/nm-dbus-manager.h28
-rw-r--r--src/nm-dbus-object.c20
-rw-r--r--src/nm-dbus-object.h20
-rw-r--r--src/nm-dbus-utils.c20
-rw-r--r--src/nm-dbus-utils.h20
-rw-r--r--src/nm-dcb.c18
-rw-r--r--src/nm-dcb.h18
-rw-r--r--src/nm-dhcp4-config.c18
-rw-r--r--src/nm-dhcp4-config.h18
-rw-r--r--src/nm-dhcp6-config.c18
-rw-r--r--src/nm-dhcp6-config.h18
-rw-r--r--src/nm-dispatcher.c18
-rw-r--r--src/nm-dispatcher.h18
-rw-r--r--src/nm-firewall-manager.c580
-rw-r--r--src/nm-firewall-manager.h53
-rw-r--r--src/nm-hostname-manager.c20
-rw-r--r--src/nm-hostname-manager.h23
-rw-r--r--src/nm-iface-helper.c29
-rw-r--r--src/nm-ip4-config.c50
-rw-r--r--src/nm-ip4-config.h20
-rw-r--r--src/nm-ip6-config.c35
-rw-r--r--src/nm-ip6-config.h20
-rw-r--r--src/nm-keep-alive.c20
-rw-r--r--src/nm-keep-alive.h20
-rw-r--r--src/nm-logging.c55
-rw-r--r--src/nm-logging.h131
-rw-r--r--src/nm-manager.c149
-rw-r--r--src/nm-manager.h22
-rw-r--r--src/nm-netns.c18
-rw-r--r--src/nm-netns.h18
-rw-r--r--src/nm-pacrunner-manager.c20
-rw-r--r--src/nm-pacrunner-manager.h22
-rw-r--r--src/nm-policy.c22
-rw-r--r--src/nm-policy.h18
-rw-r--r--src/nm-proxy-config.c20
-rw-r--r--src/nm-proxy-config.h20
-rw-r--r--src/nm-rfkill-manager.c18
-rw-r--r--src/nm-rfkill-manager.h18
-rw-r--r--src/nm-session-monitor.c18
-rw-r--r--src/nm-session-monitor.h18
-rw-r--r--src/nm-sleep-monitor.c18
-rw-r--r--src/nm-sleep-monitor.h18
-rw-r--r--src/nm-test-utils-core.h18
-rw-r--r--src/nm-types.h27
-rw-r--r--src/platform/linux/nl802154.h2
-rw-r--r--src/platform/nm-fake-platform.c20
-rw-r--r--src/platform/nm-fake-platform.h18
-rw-r--r--src/platform/nm-linux-platform.c64
-rw-r--r--src/platform/nm-linux-platform.h18
-rw-r--r--src/platform/nm-netlink.c18
-rw-r--r--src/platform/nm-netlink.h18
-rw-r--r--src/platform/nm-platform-private.h18
-rw-r--r--src/platform/nm-platform-utils.c18
-rw-r--r--src/platform/nm-platform-utils.h18
-rw-r--r--src/platform/nm-platform.c39
-rw-r--r--src/platform/nm-platform.h22
-rw-r--r--src/platform/nmp-netns.c18
-rw-r--r--src/platform/nmp-netns.h18
-rw-r--r--src/platform/nmp-object.c18
-rw-r--r--src/platform/nmp-object.h18
-rw-r--r--src/platform/nmp-rules-manager.c17
-rw-r--r--src/platform/nmp-rules-manager.h17
-rw-r--r--src/platform/tests/meson.build35
-rw-r--r--src/platform/tests/monitor.c17
-rw-r--r--src/platform/tests/test-address.c17
-rw-r--r--src/platform/tests/test-cleanup.c17
-rw-r--r--src/platform/tests/test-common.c17
-rw-r--r--src/platform/tests/test-common.h17
-rw-r--r--src/platform/tests/test-link.c72
-rw-r--r--src/platform/tests/test-nmp-object.c18
-rw-r--r--src/platform/tests/test-platform-general.c18
-rw-r--r--src/platform/tests/test-route.c17
-rw-r--r--src/platform/wifi/nm-wifi-utils-nl80211.c21
-rw-r--r--src/platform/wifi/nm-wifi-utils-nl80211.h18
-rw-r--r--src/platform/wifi/nm-wifi-utils-private.h18
-rw-r--r--src/platform/wifi/nm-wifi-utils-wext.c18
-rw-r--r--src/platform/wifi/nm-wifi-utils-wext.h18
-rw-r--r--src/platform/wifi/nm-wifi-utils.c18
-rw-r--r--src/platform/wifi/nm-wifi-utils.h18
-rw-r--r--src/platform/wpan/nm-wpan-utils.c18
-rw-r--r--src/platform/wpan/nm-wpan-utils.h18
-rw-r--r--src/ppp/meson.build25
-rw-r--r--src/ppp/nm-ppp-manager-call.c18
-rw-r--r--src/ppp/nm-ppp-manager-call.h18
-rw-r--r--src/ppp/nm-ppp-manager.c22
-rw-r--r--src/ppp/nm-ppp-manager.h18
-rw-r--r--src/ppp/nm-ppp-plugin-api.h18
-rw-r--r--src/ppp/nm-ppp-status.h20
-rw-r--r--src/ppp/nm-pppd-plugin.c18
-rw-r--r--src/ppp/nm-pppd-plugin.h18
-rw-r--r--src/settings/nm-agent-manager.c51
-rw-r--r--src/settings/nm-agent-manager.h18
-rw-r--r--src/settings/nm-secret-agent.c743
-rw-r--r--src/settings/nm-secret-agent.h24
-rw-r--r--src/settings/nm-settings-connection.c22
-rw-r--r--src/settings/nm-settings-connection.h22
-rw-r--r--src/settings/nm-settings-plugin.c18
-rw-r--r--src/settings/nm-settings-plugin.h18
-rw-r--r--src/settings/nm-settings-storage.c18
-rw-r--r--src/settings/nm-settings-storage.h18
-rw-r--r--src/settings/nm-settings-utils.c18
-rw-r--r--src/settings/nm-settings-utils.h18
-rw-r--r--src/settings/nm-settings.c67
-rw-r--r--src/settings/nm-settings.h23
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build37
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h20
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c19
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h19
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c609
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h18
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c18
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h18
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c20
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h20
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c47
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h18
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c41
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h30
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/meson.build5
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp9
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes4
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c73
-rw-r--r--src/settings/plugins/ifupdown/meson.build29
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c21
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h21
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.c21
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.h21
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c23
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.h21
-rw-r--r--src/settings/plugins/ifupdown/tests/meson.build3
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c105
-rw-r--r--src/settings/plugins/ifupdown/tests/test22-duplicate-stanzas8
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c55
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.h18
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c18
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.h18
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.c18
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.h18
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c52
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h32
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c33
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.h18
-rw-r--r--src/settings/plugins/keyfile/tests/meson.build3
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile-settings.c20
-rw-r--r--src/supplicant/nm-supplicant-config.c46
-rw-r--r--src/supplicant/nm-supplicant-config.h18
-rw-r--r--src/supplicant/nm-supplicant-interface.c20
-rw-r--r--src/supplicant/nm-supplicant-interface.h18
-rw-r--r--src/supplicant/nm-supplicant-manager.c18
-rw-r--r--src/supplicant/nm-supplicant-manager.h18
-rw-r--r--src/supplicant/nm-supplicant-settings-verify.c20
-rw-r--r--src/supplicant/nm-supplicant-settings-verify.h18
-rw-r--r--src/supplicant/nm-supplicant-types.h18
-rw-r--r--src/supplicant/tests/meson.build3
-rw-r--r--src/supplicant/tests/test-supplicant-config.c22
-rw-r--r--src/systemd/meson.build87
-rw-r--r--src/systemd/nm-sd-utils-core.c17
-rw-r--r--src/systemd/nm-sd-utils-core.h17
-rw-r--r--src/systemd/nm-sd-utils-dhcp.c18
-rw-r--r--src/systemd/nm-sd-utils-dhcp.h18
-rw-r--r--src/systemd/nm-sd.c79
-rw-r--r--src/systemd/nm-sd.h16
-rw-r--r--src/systemd/sd-adapt-core/nm-sd-adapt-core.c16
-rw-r--r--src/systemd/sd-adapt-core/nm-sd-adapt-core.h17
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-internal.h14
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-lease-internal.h3
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-network.c9
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-option.c139
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-packet.c7
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-network.c1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c21
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.c2
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.c2
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c37
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.h9
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c175
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c67
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c66
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4acd.c1
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c1
-rw-r--r--src/systemd/src/libsystemd/sd-event/sd-event.c8
-rw-r--r--src/systemd/src/libsystemd/sd-id128/sd-id128.c2
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h8
-rw-r--r--src/systemd/src/systemd/sd-dhcp-lease.h1
-rw-r--r--src/systemd/src/systemd/sd-dhcp-option.h38
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h5
-rw-r--r--src/tests/config/meson.build7
-rw-r--r--src/tests/config/nm-test-device.c20
-rw-r--r--src/tests/config/nm-test-device.h20
-rw-r--r--src/tests/config/test-config.c20
-rw-r--r--src/tests/meson.build20
-rw-r--r--src/tests/test-core-with-expect.c19
-rw-r--r--src/tests/test-core.c24
-rw-r--r--src/tests/test-dcb.c16
-rw-r--r--src/tests/test-ip4-config.c16
-rw-r--r--src/tests/test-ip6-config.c16
-rw-r--r--src/tests/test-systemd.c15
-rw-r--r--src/tests/test-utils.c16
-rw-r--r--src/tests/test-wired-defname.c16
-rw-r--r--src/vpn/nm-vpn-connection.c23
-rw-r--r--src/vpn/nm-vpn-connection.h18
-rw-r--r--src/vpn/nm-vpn-manager.c18
-rw-r--r--src/vpn/nm-vpn-manager.h18
440 files changed, 12078 insertions, 13821 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index c33f36ec..3f2f44d3 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -1,21 +1,7 @@
-/* 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 2004 - 2016 Red Hat, Inc.
- * Copyright 2005 - 2008 Novell, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2004 - 2016 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
 #include "nm-default.h"
@@ -960,9 +946,10 @@ nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule,
 
 struct _NMShutdownWaitObjHandle {
 	CList lst;
-	GObject *watched_obj;
+	gpointer watched_obj;
 	char *msg_reason;
 	bool free_msg_reason:1;
+	bool is_cancellable:1;
 };
 
 static CList _shutdown_waitobj_lst_head;
@@ -981,7 +968,7 @@ _shutdown_waitobj_unregister (NMShutdownWaitObjHandle *handle)
 
 static void
 _shutdown_waitobj_cb (gpointer user_data,
-                       GObject *where_the_object_was)
+                      GObject *where_the_object_was)
 {
 	NMShutdownWaitObjHandle *handle = user_data;
 
@@ -994,6 +981,9 @@ _shutdown_waitobj_cb (gpointer user_data,
  * nm_shutdown_wait_obj_register_full:
  * @watched_obj: the object to watch. Takes a weak reference on the object
  *   to be notified when it gets destroyed.
+ *   If wait_type is %NM_SHUTDOWN_WAIT_TYPE_HANDLE, this must be %NULL.
+ * @wait_type: whether @watched_obj is just a plain GObject or a GCancellable
+ *   that should be cancelled.
  * @msg_reason: a reason message, for debugging and logging purposes.
  * @free_msg_reason: if %TRUE, then ownership of @msg_reason will be taken
  *   and the string will be freed with g_free() afterwards. If %FALSE,
@@ -1007,21 +997,43 @@ _shutdown_waitobj_cb (gpointer user_data,
  * the reference-counter of @watched_obj as signal, that the object
  * is still used.
  *
+ * If @wait_type is %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE, then during shutdown
+ * (after %NM_SHUTDOWN_TIMEOUT_MS), the cancellable will be cancelled to notify
+ * the source of the shutdown. Note that otherwise, in this mode also @watched_obj
+ * is only tracked with a weak-pointer. Especially, it does not register to the
+ * "cancelled" signal to automatically unregister (otherwise, you would never
+ * know whether the returned NMShutdownWaitObjHandle is still valid.
+ *
  * FIXME(shutdown): proper shutdown is not yet implemented, and registering
  *   an object (currently) has no effect.
  *
+ * FIXME(shutdown): during shutdown, after %NM_SHUTDOWN_TIMEOUT_MS timeout, cancel
+ *   all remaining %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE instances. Also, when somebody
+ *   enqueues a cancellable after that point, cancel it right away on an idle handler.
+ *
  * Returns: a handle to unregister the object. The caller may choose to ignore
  *   the handle, in which case, the object will be automatically unregistered,
  *   once it gets destroyed.
+ *   Note that the handle is only valid as long as @watched_obj exists. If
+ *   you plan to use it, ensure that you take care of not using it after
+ *   destroying @watched_obj.
  */
 NMShutdownWaitObjHandle *
-nm_shutdown_wait_obj_register_full (GObject *watched_obj,
+nm_shutdown_wait_obj_register_full (gpointer watched_obj,
+                                    NMShutdownWaitType wait_type,
                                     char *msg_reason,
                                     gboolean free_msg_reason)
 {
 	NMShutdownWaitObjHandle *handle;
 
-	g_return_val_if_fail (G_IS_OBJECT (watched_obj), NULL);
+	if (wait_type == NM_SHUTDOWN_WAIT_TYPE_OBJECT)
+		g_return_val_if_fail (G_IS_OBJECT (watched_obj), NULL);
+	else if (wait_type == NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE)
+		g_return_val_if_fail (G_IS_CANCELLABLE (watched_obj), NULL);
+	else if (wait_type == NM_SHUTDOWN_WAIT_TYPE_HANDLE)
+		g_return_val_if_fail (!watched_obj, NULL);
+	else
+		g_return_val_if_reached (NULL);
 
 	if (G_UNLIKELY (!_shutdown_waitobj_lst_head.next))
 		c_list_init (&_shutdown_waitobj_lst_head);
@@ -1034,9 +1046,11 @@ nm_shutdown_wait_obj_register_full (GObject *watched_obj,
 		.watched_obj     = watched_obj,
 		.msg_reason      = msg_reason,
 		.free_msg_reason = free_msg_reason,
+		.is_cancellable  = (wait_type == NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE),
 	};
 	c_list_link_tail (&_shutdown_waitobj_lst_head, &handle->lst);
-	g_object_weak_ref (watched_obj, _shutdown_waitobj_cb, handle);
+	if (watched_obj)
+		g_object_weak_ref (watched_obj, _shutdown_waitobj_cb, handle);
 	return handle;
 }
 
@@ -1045,10 +1059,11 @@ nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle)
 {
 	g_return_if_fail (handle);
 
-	nm_assert (G_IS_OBJECT (handle->watched_obj));
+	nm_assert (!handle->watched_obj || G_IS_OBJECT (handle->watched_obj));
 	nm_assert (nm_c_list_contains_entry (&_shutdown_waitobj_lst_head, handle, lst));
 
-	g_object_weak_unref (handle->watched_obj, _shutdown_waitobj_cb, handle);
+	if (handle->watched_obj)
+		g_object_weak_unref (handle->watched_obj, _shutdown_waitobj_cb, handle);
 	_shutdown_waitobj_unregister (handle);
 }
 
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 778dc001..558a262b 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -1,21 +1,7 @@
-/* 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 2004 - 2016 Red Hat, Inc.
- * Copyright 2005 - 2008 Novell, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2004 - 2016 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
 #ifndef __NETWORKMANAGER_UTILS_H__
@@ -87,13 +73,56 @@ NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *ru
 #define NM_SHUTDOWN_TIMEOUT_MS            1500
 #define NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG    500
 
+typedef enum {
+	/* There is no watched_obj argument, and the shutdown is delayed until the user
+	 * explicitly calls unregister on the returned handle. */
+	NM_SHUTDOWN_WAIT_TYPE_HANDLE,
+
+	/* The watched_obj argument is a GObject, and shutdown is delayed until the object
+	 * gets destroyed (or unregistered). */
+	NM_SHUTDOWN_WAIT_TYPE_OBJECT,
+
+	/* The watched_obj argument is a GCancellable, and shutdown is delayed until the object
+	 * gets destroyed (or unregistered). Note that after NM_SHUTDOWN_TIMEOUT_MS, the
+	 * cancellable will be cancelled to notify listeners about the shutdown. */
+	NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE,
+} NMShutdownWaitType;
+
 typedef struct _NMShutdownWaitObjHandle NMShutdownWaitObjHandle;
 
-NMShutdownWaitObjHandle *nm_shutdown_wait_obj_register_full (GObject *watched_obj,
+NMShutdownWaitObjHandle *nm_shutdown_wait_obj_register_full (gpointer watched_obj,
+                                                             NMShutdownWaitType wait_type,
                                                              char *msg_reason,
                                                              gboolean free_msg_reason);
 
-#define nm_shutdown_wait_obj_register(watched_obj, msg_reason) nm_shutdown_wait_obj_register_full((watched_obj), (""msg_reason""), FALSE)
+static inline NMShutdownWaitObjHandle *
+nm_shutdown_wait_obj_register_object_full (gpointer watched_obj,
+                                           char *msg_reason,
+                                           gboolean free_msg_reason)
+{
+	return nm_shutdown_wait_obj_register_full (watched_obj, NM_SHUTDOWN_WAIT_TYPE_OBJECT, msg_reason, free_msg_reason);
+}
+
+#define nm_shutdown_wait_obj_register_object(watched_obj, msg_reason) nm_shutdown_wait_obj_register_object_full((watched_obj), (""msg_reason""), FALSE)
+
+static inline NMShutdownWaitObjHandle *
+nm_shutdown_wait_obj_register_handle_full (char *msg_reason,
+                                           gboolean free_msg_reason)
+{
+	return nm_shutdown_wait_obj_register_full (NULL, NM_SHUTDOWN_WAIT_TYPE_HANDLE, msg_reason, free_msg_reason);
+}
+
+#define nm_shutdown_wait_obj_register_handle(msg_reason) nm_shutdown_wait_obj_register_handle_full((""msg_reason""), FALSE)
+
+static inline NMShutdownWaitObjHandle *
+nm_shutdown_wait_obj_register_cancellable_full (GCancellable *watched_obj,
+                                                char *msg_reason,
+                                                gboolean free_msg_reason)
+{
+	return nm_shutdown_wait_obj_register_full (watched_obj, NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE, msg_reason, free_msg_reason);
+}
+
+#define nm_shutdown_wait_obj_register_cancellable(watched_obj, msg_reason) nm_shutdown_wait_obj_register_cancellable_full((watched_obj), (""msg_reason""), FALSE)
 
 void nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle);
 
diff --git a/src/devices/adsl/meson.build b/src/devices/adsl/meson.build
index f92e809c..a5d1c4b4 100644
--- a/src/devices/adsl/meson.build
+++ b/src/devices/adsl/meson.build
@@ -3,15 +3,11 @@ sources = files(
   'nm-device-adsl.c',
 )
 
-deps = [
-  libudev_dep,
-  nm_dep,
-]
-
 libnm_device_plugin_adsl = shared_module(
   'nm-device-plugin-adsl',
   sources: sources,
-  dependencies: deps,
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
@@ -25,10 +21,3 @@ test(
   check_exports,
   args: [libnm_device_plugin_adsl.full_path(), linker_script_devices],
 )
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-adsl: src/devices/adsl/libnm-device-plugin-adsl.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/adsl/.libs/libnm-device-plugin-adsl.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/adsl/.libs/libnm-device-plugin-adsl.so)
-'''
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index bc0bf5c6..487011ff 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2013 Red Hat, Inc.
  */
 
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index 34efdb01..7c2b3e20 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Pantelis Koukousoulas <pktoss@gmail.com>
  */
 
diff --git a/src/devices/adsl/nm-device-adsl.h b/src/devices/adsl/nm-device-adsl.h
index 2d3a9fc7..84176fae 100644
--- a/src/devices/adsl/nm-device-adsl.h
+++ b/src/devices/adsl/nm-device-adsl.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Author: Pantelis Koukousoulas <pktoss@gmail.com>
  * Copyright (C) 2009 - 2011 Red Hat Inc.
  */
diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build
index b2f67ceb..f0507c23 100644
--- a/src/devices/bluetooth/meson.build
+++ b/src/devices/bluetooth/meson.build
@@ -1,16 +1,12 @@
 sources = files(
-  'nm-bluez-device.c',
   'nm-bluez-manager.c',
-  'nm-bluez4-adapter.c',
-  'nm-bluez4-manager.c',
-  'nm-bluez5-manager.c',
   'nm-bt-error.c',
   'nm-device-bt.c',
 )
 
 deps = [
+  daemon_nm_default_dep,
   libnm_wwan_dep,
-  nm_dep,
 ]
 
 if enable_bluez5_dun
@@ -19,9 +15,21 @@ if enable_bluez5_dun
   deps += bluez5_dep
 endif
 
+libnm_device_plugin_bluetooth_static = static_library(
+  'nm-device-plugin-bluetooth-static',
+  sources: sources,
+  dependencies: deps,
+  c_args: daemon_c_flags,
+)
+
+libnm_device_plugin_bluetooth_static_dep = declare_dependency(
+  link_whole: libnm_device_plugin_bluetooth_static,
+)
+
+deps += libnm_device_plugin_bluetooth_static_dep
+
 libnm_device_plugin_bluetooth = shared_module(
   'nm-device-plugin-bluetooth',
-  sources: sources,
   dependencies: deps,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
@@ -38,9 +46,13 @@ test(
   args: [libnm_device_plugin_bluetooth.full_path(), linker_script_devices],
 )
 
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-bluetooth: src/devices/bluetooth/libnm-device-plugin-bluetooth.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/bluetooth/.libs/libnm-device-plugin-bluetooth.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/bluetooth/.libs/libnm-device-plugin-bluetooth.so)
-'''
+if enable_tests
+  test_unit = 'nm-bt-test'
+
+  executable(
+    test_unit,
+    'tests/' + test_unit + '.c',
+    dependencies: [ libnetwork_manager_test_dep, deps ],
+    c_args: test_c_flags,
+  )
+endif
diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h
index 956375bb..ade4434b 100644
--- a/src/devices/bluetooth/nm-bluez-common.h
+++ b/src/devices/bluetooth/nm-bluez-common.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
@@ -26,19 +12,12 @@
 #define NM_BLUEZ_SERVICE           "org.bluez"
 
 #define NM_BLUEZ_MANAGER_PATH      "/"
-#define NM_OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
 
 #define NM_BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1"
 #define NM_BLUEZ5_DEVICE_INTERFACE  "org.bluez.Device1"
 #define NM_BLUEZ5_NETWORK_INTERFACE "org.bluez.Network1"
 #define NM_BLUEZ5_NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1"
 
-#define NM_BLUEZ4_MANAGER_INTERFACE "org.bluez.Manager"
-#define NM_BLUEZ4_ADAPTER_INTERFACE "org.bluez.Adapter"
-#define NM_BLUEZ4_DEVICE_INTERFACE  "org.bluez.Device"
-#define NM_BLUEZ4_SERIAL_INTERFACE  "org.bluez.Serial"
-#define NM_BLUEZ4_NETWORK_INTERFACE "org.bluez.Network"
-
 #define NM_BLUEZ_MANAGER_BDADDR_ADDED "bdaddr-added"
 #define NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED "network-server-added"
 
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
deleted file mode 100644
index fb0a72c4..00000000
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ /dev/null
@@ -1,1320 +0,0 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2013 Intel Corporation.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez-device.h"
-
-#include "nm-core-internal.h"
-#include "nm-bt-error.h"
-#include "nm-bluez-common.h"
-#include "settings/nm-settings.h"
-#include "settings/nm-settings-connection.h"
-#include "NetworkManagerUtils.h"
-
-#if WITH_BLUEZ5_DUN
-#include "nm-bluez5-dun.h"
-#endif
-
-/*****************************************************************************/
-
-#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) ))
-
-/*****************************************************************************/
-
-NM_GOBJECT_PROPERTIES_DEFINE (NMBluezDevice,
-	PROP_PATH,
-	PROP_ADDRESS,
-	PROP_NAME,
-	PROP_CAPABILITIES,
-	PROP_USABLE,
-	PROP_CONNECTED,
-);
-
-enum {
-	INITIALIZED,
-	REMOVED,
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
-	char *path;
-	GDBusConnection *dbus_connection;
-
-	GDBusProxy *proxy;
-
-	GDBusProxy *adapter5;
-	gboolean adapter_powered;
-
-	int bluez_version;
-
-	gboolean initialized;
-	gboolean usable;
-	NMBluetoothCapabilities connection_bt_type;
-
-	guint check_emit_usable_id;
-
-	char *adapter_address;
-	char *address;
-	char *name;
-	guint32 capabilities;
-	gboolean connected;
-	gboolean paired;
-
-	char *b4_iface;
-#if WITH_BLUEZ5_DUN
-	NMBluez5DunContext *b5_dun_context;
-#endif
-
-	NMSettings *settings;
-	GSList *connections;
-
-	NMSettingsConnection *pan_connection;
-	gboolean pan_connection_no_autocreate;
-} NMBluezDevicePrivate;
-
-struct _NMBluezDevice {
-	GObject parent;
-	NMBluezDevicePrivate _priv;
-};
-
-struct _NMBluezDeviceClass {
-	GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
-
-#define NM_BLUEZ_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluezDevice, NM_IS_BLUEZ_DEVICE)
-
-/*****************************************************************************/
-
-static void cp_connection_added (NMSettings *settings,
-                                 NMSettingsConnection *sett_conn,
-                                 NMBluezDevice *self);
-static gboolean connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn);
-
-/*****************************************************************************/
-
-const char *
-nm_bluez_device_get_path (NMBluezDevice *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
-	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path;
-}
-
-const char *
-nm_bluez_device_get_address (NMBluezDevice *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
-	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address;
-}
-
-gboolean
-nm_bluez_device_get_initialized (NMBluezDevice *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
-	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized;
-}
-
-gboolean
-nm_bluez_device_get_usable (NMBluezDevice *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
-	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable;
-}
-
-const char *
-nm_bluez_device_get_name (NMBluezDevice *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
-	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name;
-}
-
-guint32
-nm_bluez_device_get_capabilities (NMBluezDevice *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0);
-
-	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities;
-}
-
-gboolean
-nm_bluez_device_get_connected (NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv;
-
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
-	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	return priv->connected;
-}
-
-static void
-pan_connection_check_create (NMBluezDevice *self)
-{
-	gs_unref_object NMConnection *connection = NULL;
-	NMSettingsConnection *added;
-	NMSetting *setting;
-	gs_free char *id = NULL;
-	char uuid[37];
-	GError *error = NULL;
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	g_return_if_fail (priv->capabilities & NM_BT_CAPABILITY_NAP);
-	g_return_if_fail (priv->connections == NULL);
-	g_return_if_fail (priv->name);
-
-	if (priv->pan_connection || priv->pan_connection_no_autocreate) {
-		/* already have a connection or we don't want to create one, nothing to do. */
-		return;
-	}
-
-	/* Only try once to create a connection. If it does not succeed, we do not try again. Also,
-	 * if the connection gets deleted later, do not create another one for this device. */
-	priv->pan_connection_no_autocreate = TRUE;
-
-	/* create a new connection */
-
-	connection = nm_simple_connection_new ();
-
-	/* Setting: Connection */
-	nm_utils_uuid_generate_buf (uuid);
-	id = g_strdup_printf (_("%s Network"), priv->name);
-	setting = nm_setting_connection_new ();
-	g_object_set (setting,
-	              NM_SETTING_CONNECTION_ID, id,
-	              NM_SETTING_CONNECTION_UUID, uuid,
-	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
-	              NULL);
-	nm_connection_add_setting (connection, setting);
-
-	/* Setting: Bluetooth */
-	setting = nm_setting_bluetooth_new ();
-	g_object_set (G_OBJECT (setting),
-	              NM_SETTING_BLUETOOTH_BDADDR, priv->address,
-	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
-	              NULL);
-	nm_connection_add_setting (connection, setting);
-
-	if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
-		nm_log_err (LOGD_BT, "bluez[%s] couldn't generate a connection for NAP device: %s",
-		            priv->path, error->message);
-		g_error_free (error);
-		g_return_if_reached ();
-	}
-
-	/* 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->settings, cp_connection_added, self);
-	nm_settings_add_connection (priv->settings,
-	                            connection,
-	                            NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
-	                            NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
-	                            NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
-	                            &added,
-	                            &error);
-	g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
-
-	if (added) {
-		nm_assert (!g_slist_find (priv->connections, added));
-		nm_assert (connection_compatible (self, added));
-		priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
-		priv->pan_connection = added;
-		nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid);
-	} else {
-		nm_log_warn (LOGD_BT, "bluez[%s] couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
-		             priv->path, id, uuid, error->message);
-		g_clear_error (&error);
-	}
-}
-
-static gboolean
-check_emit_usable (NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	gboolean new_usable;
-
-	/* only expect the supported capabilities set. */
-	nm_assert ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE );
-
-	new_usable = (   priv->initialized && priv->capabilities
-	              && priv->name && priv->paired
-	              && (   (priv->bluez_version == 4)
-	                  || (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered))
-	              && priv->dbus_connection && priv->address && priv->adapter_address);
-
-	if (!new_usable)
-		goto END;
-
-	if (priv->connections)
-		goto END;
-
-	if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) {
-		/* non NAP devices are only usable, if they already have a connection. */
-		new_usable = FALSE;
-		goto END;
-	}
-
-	pan_connection_check_create (self);
-	new_usable = !!priv->pan_connection;
-
-END:
-	if (new_usable != priv->usable) {
-		priv->usable = new_usable;
-		_notify (self, PROP_USABLE);
-	}
-
-	return G_SOURCE_REMOVE;
-}
-
-static void
-check_emit_usable_schedule (NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	if (priv->check_emit_usable_id == 0)
-		priv->check_emit_usable_id = g_idle_add ((GSourceFunc) check_emit_usable, self);
-}
-
-/*****************************************************************************/
-
-static gboolean
-connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
-	NMSettingBluetooth *s_bt;
-	const char *bt_type;
-	const char *bdaddr;
-
-	if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
-		return FALSE;
-
-	s_bt = nm_connection_get_setting_bluetooth (connection);
-	if (!s_bt)
-		return FALSE;
-
-	if (!priv->address)
-		return FALSE;
-
-	bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
-	if (!bdaddr)
-		return FALSE;
-	if (!nm_utils_hwaddr_matches (bdaddr, -1, priv->address, -1))
-		return FALSE;
-
-	bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
-
-	if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_NAP))
-		return FALSE;
-
-	if (   g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
-	    && !(priv->capabilities & NM_BT_CAPABILITY_DUN))
-		return FALSE;
-
-	if (   g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)
-	    && !(priv->capabilities & NM_BT_CAPABILITY_NAP))
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean
-_internal_track_connection (NMBluezDevice *self,
-                            NMSettingsConnection *sett_conn,
-                            gboolean tracked)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	gboolean was_tracked;
-
-	was_tracked = !!g_slist_find (priv->connections, sett_conn);
-	if (was_tracked == !!tracked)
-		return FALSE;
-
-	if (tracked)
-		priv->connections = g_slist_prepend (priv->connections, g_object_ref (sett_conn));
-	else {
-		priv->connections = g_slist_remove (priv->connections, sett_conn);
-		if (priv->pan_connection == sett_conn)
-			priv->pan_connection = NULL;
-		g_object_unref (sett_conn);
-	}
-
-	return TRUE;
-}
-
-static void
-cp_connection_added (NMSettings *settings,
-                     NMSettingsConnection *sett_conn,
-                     NMBluezDevice *self)
-{
-	if (connection_compatible (self, sett_conn)) {
-		if (_internal_track_connection (self, sett_conn, TRUE))
-			check_emit_usable (self);
-	}
-}
-
-static void
-cp_connection_removed (NMSettings *settings,
-                       NMSettingsConnection *sett_conn,
-                       NMBluezDevice *self)
-{
-	if (_internal_track_connection (self, sett_conn, FALSE))
-		check_emit_usable (self);
-}
-
-static void
-cp_connection_updated (NMSettings *settings,
-                       NMSettingsConnection *sett_conn,
-                       guint update_reason_u,
-                       NMBluezDevice *self)
-{
-	if (_internal_track_connection (self, sett_conn,
-	                                connection_compatible (self, sett_conn)))
-		check_emit_usable_schedule (self);
-}
-
-static void
-load_connections (NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	NMSettingsConnection *const*connections;
-	guint i;
-	gboolean changed = FALSE;
-
-	connections = nm_settings_get_connections (priv->settings, NULL);
-	for (i = 0; connections[i]; i++) {
-		if (connection_compatible (self, connections[i]))
-			changed |= _internal_track_connection (self, connections[i], TRUE);
-	}
-	if (changed)
-		check_emit_usable (self);
-}
-
-/*****************************************************************************/
-
-static void
-bluez_disconnect_cb (GDBusConnection *dbus_connection,
-                     GAsyncResult *res,
-                     gpointer user_data)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) user_data);
-	GError *error = NULL;
-	GVariant *variant;
-
-	variant = g_dbus_connection_call_finish (dbus_connection, res, &error);
-	if (!variant) {
-		if (!strstr (error->message, "org.bluez.Error.NotConnected"))
-			nm_log_warn (LOGD_BT, "bluez[%s]: failed to disconnect: %s", priv->path, error->message);
-		g_error_free (error);
-	} else
-		g_variant_unref (variant);
-
-	g_object_unref (NM_BLUEZ_DEVICE (user_data));
-}
-
-void
-nm_bluez_device_disconnect (NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GVariant *args = NULL;
-	const char *dbus_iface = NULL;
-
-	g_return_if_fail (priv->dbus_connection);
-
-	/* FIXME: if we are in the process of connecting and cancel the
-	 * connection attempt, we must complete the pending connect request.
-	 * However, we must also ensure that we don't leave a connected device. */
-	if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
-		if (priv->bluez_version == 4) {
-			/* Can't pass a NULL interface name through dbus to bluez, so just
-			 * ignore the disconnect if the interface isn't known.
-			 */
-			if (!priv->b4_iface)
-				goto out;
-			args = g_variant_new ("(s)", priv->b4_iface),
-			dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE;
-		} else if (priv->bluez_version == 5) {
-#if WITH_BLUEZ5_DUN
-			nm_bluez5_dun_cleanup (priv->b5_dun_context);
-#endif
-			priv->connected = FALSE;
-			goto out;
-		}
-	} else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) {
-		if (priv->bluez_version == 4)
-			dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE;
-		else if (priv->bluez_version == 5)
-			dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
-		else
-			g_assert_not_reached ();
-	} else
-		g_assert_not_reached ();
-
-	g_dbus_connection_call (priv->dbus_connection,
-	                        NM_BLUEZ_SERVICE,
-	                        priv->path,
-	                        dbus_iface,
-	                        "Disconnect",
-	                        args ?: g_variant_new("()"),
-	                        NULL,
-	                        G_DBUS_CALL_FLAGS_NONE,
-	                        10000,
-	                        NULL,
-	                        (GAsyncReadyCallback) bluez_disconnect_cb,
-	                        g_object_ref (self));
-
-out:
-	g_clear_pointer (&priv->b4_iface, g_free);
-	priv->connection_bt_type = NM_BT_CAPABILITY_NONE;
-}
-
-static void
-_connect_complete (NMBluezDevice *self,
-                   const char *device,
-                   NMBluezDeviceConnectCallback callback,
-                   gpointer callback_user_data,
-                   GError *error)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	nm_assert ((device || error) && !(device && error));
-
-	if (   device
-	    && priv->bluez_version == 5) {
-		priv->connected = TRUE;
-		_notify (self, PROP_CONNECTED);
-	}
-
-	if (callback)
-		callback (self, device, error, callback_user_data);
-}
-
-static void
-_connect_cb (GObject *source_object,
-                  GAsyncResult *res,
-                  gpointer user_data)
-{
-	gs_unref_object NMBluezDevice *self = NULL;
-	NMBluezDevicePrivate *priv;
-	NMBluezDeviceConnectCallback callback;
-	gpointer callback_user_data;
-	gs_free_error GError *error = NULL;
-	char *device = NULL;
-	gs_unref_variant GVariant *variant = NULL;
-
-	nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
-
-	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	variant = _nm_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, G_VARIANT_TYPE ("(s)"), &error);
-	if (variant) {
-		g_variant_get (variant, "(s)", &device);
-		priv->b4_iface = device;
-	}
-
-	_connect_complete (self, device, callback, callback_user_data, error);
-}
-
-#if WITH_BLUEZ5_DUN
-static void
-_connect_cb_bluez5_dun (NMBluez5DunContext *context,
-                        const char *device,
-                        GError *error,
-                        gpointer user_data)
-{
-	gs_unref_object NMBluezDevice *self = NULL;
-	gs_unref_object GCancellable *cancellable = NULL;
-	NMBluezDeviceConnectCallback callback;
-	gpointer callback_user_data;
-	gs_free_error GError *cancelled_error = NULL;
-
-	nm_utils_user_data_unpack (user_data, &self, &cancellable, &callback, &callback_user_data);
-
-	/* FIXME(shutdown): the async operation nm_bluez5_dun_connect() should be cancellable.
-	 * Fake it here. */
-	if (g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error))
-		error = cancelled_error;
-
-	_connect_complete (self, device, callback, callback_user_data, error);
-}
-#else /* WITH_BLUEZ5_DUN */
-static void
-_connect_cb_bluez5_dun_idle_no_b5 (gpointer user_data,
-                                   GCancellable *cancellable)
-{
-	gs_unref_object NMBluezDevice *self = NULL;
-	NMBluezDeviceConnectCallback callback;
-	gpointer callback_user_data;
-	gs_free_error GError *error = NULL;
-
-	nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
-
-	if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) {
-		g_set_error (&error,
-		             NM_BT_ERROR,
-		             NM_BT_ERROR_DUN_CONNECT_FAILED,
-		             "NetworkManager built without support for Bluez 5");
-	}
-	callback (self, NULL, error, callback_user_data);
-}
-#endif /* WITH_BLUEZ5_DUN */
-
-void
-nm_bluez_device_connect_async (NMBluezDevice *self,
-                               NMBluetoothCapabilities connection_bt_type,
-                               GCancellable *cancellable,
-                               NMBluezDeviceConnectCallback callback,
-                               gpointer callback_user_data)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	const char *dbus_iface = NULL;
-	const char *connect_type = NULL;
-
-	g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
-
-	priv->connection_bt_type = connection_bt_type;
-
-	if (connection_bt_type == NM_BT_CAPABILITY_NAP) {
-		connect_type = BLUETOOTH_CONNECT_NAP;
-		if (priv->bluez_version == 4)
-			dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE;
-		else if (priv->bluez_version == 5)
-			dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
-	} else if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
-		connect_type = BLUETOOTH_CONNECT_DUN;
-		if (priv->bluez_version == 4)
-			dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE;
-		else if (priv->bluez_version == 5) {
-#if WITH_BLUEZ5_DUN
-			if (priv->b5_dun_context == NULL)
-				priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address);
-			nm_bluez5_dun_connect (priv->b5_dun_context,
-			                       _connect_cb_bluez5_dun,
-			                       nm_utils_user_data_pack (g_object_ref (self),
-			                                                nm_g_object_ref (cancellable),
-			                                                callback,
-			                                                callback_user_data));
-#else
-			if (callback) {
-				nm_utils_invoke_on_idle (_connect_cb_bluez5_dun_idle_no_b5,
-				                         nm_utils_user_data_pack (g_object_ref (self),
-				                                                  callback,
-				                                                  callback_user_data),
-				                         cancellable);
-			}
-#endif
-			return;
-		}
-	} else
-		g_return_if_reached ();
-
-	/* FIXME: we need to remember that a connect is in progress.
-	 * So, if the request gets cancelled, that we disconnect the
-	 * connection that was established in the meantime. */
-	g_dbus_connection_call (priv->dbus_connection,
-	                        NM_BLUEZ_SERVICE,
-	                        priv->path,
-	                        dbus_iface,
-	                        "Connect",
-	                        g_variant_new ("(s)", connect_type),
-	                        NULL,
-	                        G_DBUS_CALL_FLAGS_NONE,
-	                        20000,
-	                        cancellable,
-	                        _connect_cb,
-	                        nm_utils_user_data_pack (g_object_ref (self),
-	                                                 callback,
-	                                                 callback_user_data));
-}
-
-/*****************************************************************************/
-
-static void
-set_adapter_address (NMBluezDevice *self, const char *address)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	g_return_if_fail (address);
-
-	if (priv->adapter_address)
-		g_free (priv->adapter_address);
-	priv->adapter_address = g_strdup (address);
-}
-
-static guint32
-convert_uuids_to_capabilities (const char **strings)
-{
-	const char **iter;
-	guint32 capabilities = 0;
-
-	for (iter = strings; iter && *iter; iter++) {
-		char **parts;
-
-		parts = g_strsplit (*iter, "-", -1);
-		if (parts && parts[0]) {
-			switch (g_ascii_strtoull (parts[0], NULL, 16)) {
-			case 0x1103:
-				capabilities |= NM_BT_CAPABILITY_DUN;
-				break;
-			case 0x1116:
-				capabilities |= NM_BT_CAPABILITY_NAP;
-				break;
-			default:
-				break;
-			}
-		}
-		g_strfreev (parts);
-	}
-
-	return capabilities;
-}
-
-static void
-_set_property_capabilities (NMBluezDevice *self, const char **uuids)
-{
-	guint32 uint_val;
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	uint_val = convert_uuids_to_capabilities (uuids);
-	if (priv->capabilities != uint_val) {
-		if (priv->capabilities) {
-			/* changing (relevant) capabilities is not supported and ignored -- except setting initially */
-			nm_log_warn (LOGD_BT, "bluez[%s] ignore change of capabilities for Bluetooth device from %u to %u",
-			             priv->path, priv->capabilities, uint_val);
-			return;
-		}
-		nm_log_dbg (LOGD_BT, "bluez[%s] set capabilities for Bluetooth device: %s%s%s", priv->path,
-		            uint_val & NM_BT_CAPABILITY_NAP ? "NAP" : "",
-		            ((uint_val & NM_BT_CAPABILITY_DUN) && (uint_val &NM_BT_CAPABILITY_NAP)) ? " | " : "",
-		            uint_val & NM_BT_CAPABILITY_DUN ? "DUN" : "");
-		priv->capabilities = uint_val;
-		_notify (self, PROP_CAPABILITIES);
-	}
-}
-
-/**
- * priv->address can only be set one to a certain (non NULL) value. Every later attempt
- * to reset it to another value will be ignored and a warning will be logged.
- **/
-static void
-_set_property_address (NMBluezDevice *self, const char *addr)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	if (g_strcmp0 (priv->address, addr) == 0)
-		return;
-
-	if (!addr) {
-		nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to NULL", priv->path, priv->address);
-		return;
-	}
-
-	if (priv->address != NULL) {
-		nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to '%s'", priv->path, priv->address, addr);
-		return;
-	}
-
-	if (!nm_utils_hwaddr_valid (addr, ETH_ALEN)) {
-		nm_log_warn (LOGD_BT, "bluez[%s] cannot set address to '%s' (invalid value)", priv->path, addr);
-		return;
-	}
-
-	priv->address = g_strdup (addr);
-	_notify (self, PROP_ADDRESS);
-}
-
-static void
-_take_variant_property_address (NMBluezDevice *self, GVariant *v)
-{
-	_set_property_address (self, VARIANT_IS_OF_TYPE_STRING (v) ? g_variant_get_string (v, NULL) : NULL);
-	if (v)
-		g_variant_unref (v);
-}
-
-static void
-_take_variant_property_name (NMBluezDevice *self, GVariant *v)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	const char *str;
-
-	if (VARIANT_IS_OF_TYPE_STRING (v)) {
-		str = g_variant_get_string (v, NULL);
-		if (g_strcmp0 (priv->name, str)) {
-			g_free (priv->name);
-			priv->name = g_strdup (str);
-			_notify (self, PROP_NAME);
-		}
-	}
-	if (v)
-		g_variant_unref (v);
-}
-
-static void
-_take_variant_property_uuids (NMBluezDevice *self, GVariant *v)
-{
-	if (VARIANT_IS_OF_TYPE_STRING_ARRAY (v)) {
-		const char **uuids = g_variant_get_strv (v, NULL);
-
-		_set_property_capabilities (self, uuids);
-		g_free (uuids);
-	}
-	if (v)
-		g_variant_unref (v);
-}
-
-static void
-_take_variant_property_connected (NMBluezDevice *self, GVariant *v)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	if (VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
-		gboolean connected = g_variant_get_boolean (v);
-
-		if (priv->connected != connected) {
-			priv->connected = connected;
-			_notify (self, PROP_CONNECTED);
-		}
-	}
-	if (v)
-		g_variant_unref (v);
-}
-
-static void
-_take_variant_property_paired (NMBluezDevice *self, GVariant *v)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	if (VARIANT_IS_OF_TYPE_BOOLEAN (v))
-		priv->paired = g_variant_get_boolean (v);
-
-	if (v)
-		g_variant_unref (v);
-}
-
-static void
-adapter5_on_properties_changed (GDBusProxy *proxy,
-                                GVariant *changed_properties,
-                                GStrv invalidated_properties,
-                                gpointer user_data)
-{
-	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GVariantIter i;
-	const char *property;
-	GVariant *v;
-
-	g_variant_iter_init (&i, changed_properties);
-	while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
-		if (!strcmp (property, "Powered") && VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
-			gboolean powered = g_variant_get_boolean (v);
-			if (priv->adapter_powered != powered)
-				priv->adapter_powered = powered;
-		}
-		g_variant_unref (v);
-	}
-
-	check_emit_usable (self);
-}
-
-static void
-adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GError *error = NULL;
-	GVariant *v;
-
-	priv->adapter5 = g_dbus_proxy_new_for_bus_finish (res, &error);
-	if (!priv->adapter5) {
-		nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire adapter proxy: %s.", priv->path, error->message);
-		g_clear_error (&error);
-		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
-	} else {
-		g_signal_connect (priv->adapter5, "g-properties-changed",
-		                  G_CALLBACK (adapter5_on_properties_changed), self);
-
-		/* Check adapter's powered state */
-		v = g_dbus_proxy_get_cached_property (priv->adapter5, "Powered");
-		priv->adapter_powered = VARIANT_IS_OF_TYPE_BOOLEAN (v) ? g_variant_get_boolean (v) : FALSE;
-		if (v)
-			g_variant_unref (v);
-
-		v = g_dbus_proxy_get_cached_property (priv->adapter5, "Address");
-		if (VARIANT_IS_OF_TYPE_STRING (v))
-			set_adapter_address (self, g_variant_get_string (v, NULL));
-
-		priv->initialized = TRUE;
-		g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
-
-		check_emit_usable (self);
-	}
-
-	g_object_unref (self);
-}
-
-static void
-_take_one_variant_property (NMBluezDevice *self, const char *property, GVariant *v)
-{
-	if (v) {
-		if (!g_strcmp0 (property, "Address"))
-			_take_variant_property_address (self, v);
-		else if (!g_strcmp0 (property, "Connected"))
-			_take_variant_property_connected (self, v);
-		else if (!g_strcmp0 (property, "Paired"))
-			_take_variant_property_paired (self, v);
-		else if (!g_strcmp0 (property, "Name"))
-			_take_variant_property_name (self, v);
-		else if (!g_strcmp0 (property, "UUIDs"))
-			_take_variant_property_uuids (self, v);
-		else
-			g_variant_unref (v);
-	}
-}
-
-static void
-_set_properties (NMBluezDevice *self, GVariant *properties)
-{
-	GVariantIter i;
-	const char *property;
-	GVariant *v;
-
-	g_object_freeze_notify (G_OBJECT (self));
-	g_variant_iter_init (&i, properties);
-	while (g_variant_iter_next (&i, "{&sv}", &property, &v))
-		_take_one_variant_property (self, property, v);
-	g_object_thaw_notify (G_OBJECT (self));
-}
-
-static void
-properties_changed (GDBusProxy *proxy,
-                    GVariant *changed_properties,
-                    GStrv invalidated_properties,
-                    gpointer user_data)
-{
-	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
-
-	_set_properties (self, changed_properties);
-	check_emit_usable (self);
-}
-
-static void
-bluez4_property_changed (GDBusProxy *proxy,
-                         const char *property,
-                         GVariant   *v,
-                         gpointer    user_data)
-{
-	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
-
-	_take_one_variant_property (self, property, v);
-	check_emit_usable (self);
-}
-
-static void
-get_properties_cb_4 (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GError *err = NULL;
-	GVariant *v_properties, *v_dict;
-
-	v_properties = _nm_dbus_proxy_call_finish (priv->proxy, res,
-	                                           G_VARIANT_TYPE ("(a{sv})"),
-	                                           &err);
-	if (!v_properties) {
-		g_dbus_error_strip_remote_error (err);
-		nm_log_warn (LOGD_BT, "bluez[%s] error getting device properties: %s",
-		             priv->path, err->message);
-		g_error_free (err);
-		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
-		goto END;
-	}
-
-	v_dict = g_variant_get_child_value (v_properties, 0);
-	_set_properties (self, v_dict);
-	g_variant_unref (v_dict);
-	g_variant_unref (v_properties);
-
-	/* Check if any connections match this device */
-	load_connections (self);
-
-	priv->initialized = TRUE;
-	g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
-
-	check_emit_usable (self);
-
-END:
-	g_object_unref (self);
-}
-
-static void
-query_properties (NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GVariant *v;
-
-	switch (priv->bluez_version) {
-	case 4:
-		g_dbus_proxy_call (priv->proxy, "GetProperties", NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, 3000,
-		                   NULL, get_properties_cb_4, g_object_ref (self));
-		break;
-	case 5:
-		g_object_freeze_notify (G_OBJECT (self));
-		_take_variant_property_address   (self, g_dbus_proxy_get_cached_property (priv->proxy, "Address"));
-		_take_variant_property_connected (self, g_dbus_proxy_get_cached_property (priv->proxy, "Connected"));
-		_take_variant_property_paired    (self, g_dbus_proxy_get_cached_property (priv->proxy, "Paired"));
-		_take_variant_property_name      (self, g_dbus_proxy_get_cached_property (priv->proxy, "Name"));
-		_take_variant_property_uuids     (self, g_dbus_proxy_get_cached_property (priv->proxy, "UUIDs"));
-		g_object_thaw_notify (G_OBJECT (self));
-
-		v = g_dbus_proxy_get_cached_property (priv->proxy, "Adapter");
-		if (VARIANT_IS_OF_TYPE_OBJECT_PATH (v)) {
-			g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-			                          G_DBUS_PROXY_FLAGS_NONE,
-			                          NULL,
-			                          NM_BLUEZ_SERVICE,
-			                          g_variant_get_string (v, NULL),
-			                          NM_BLUEZ5_ADAPTER_INTERFACE,
-			                          NULL,
-			                          (GAsyncReadyCallback) adapter5_on_acquired,
-			                          g_object_ref (self));
-			g_variant_unref (v);
-		} else {
-			/* If the Adapter property is unset at this point, we won't try to acquire the adapter later on
-			 * and the device stays unusable. This should not happen, but if it does, log a debug message. */
-			nm_log_dbg (LOGD_BT, "bluez[%s] device has no adapter property and cannot be used.", priv->path);
-		}
-
-		/* Check if any connections match this device */
-		load_connections (self);
-
-		break;
-	}
-}
-
-static void
-on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GError *error = NULL;
-
-	priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
-	if (!priv->proxy) {
-		nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire device proxy: %s.", priv->path, error->message);
-		g_clear_error (&error);
-		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
-	} else {
-		g_signal_connect (priv->proxy, "g-properties-changed",
-		                  G_CALLBACK (properties_changed), self);
-		if (priv->bluez_version == 4) {
-			/* Watch for custom Bluez4 PropertyChanged signals */
-			_nm_dbus_signal_connect (priv->proxy, "PropertyChanged", G_VARIANT_TYPE ("(sv)"),
-			                         G_CALLBACK (bluez4_property_changed), self);
-		}
-
-		query_properties (self);
-	}
-	g_object_unref (self);
-}
-
-static void
-on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GError *error = NULL;
-
-	priv->dbus_connection = g_bus_get_finish (res, &error);
-
-	if (!priv->dbus_connection) {
-		nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire bus connection: %s.", priv->path, error->message);
-		g_clear_error (&error);
-		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
-	} else
-		check_emit_usable (self);
-
-	g_object_unref (self);
-}
-
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
-
-	switch (prop_id) {
-	case PROP_PATH:
-		g_value_set_string (value, priv->path);
-		break;
-	case PROP_ADDRESS:
-		g_value_set_string (value, priv->address);
-		break;
-	case PROP_NAME:
-		g_value_set_string (value, priv->name);
-		break;
-	case PROP_CAPABILITIES:
-		g_value_set_uint (value, priv->capabilities);
-		break;
-	case PROP_USABLE:
-		g_value_set_boolean (value, priv->usable);
-		break;
-	case PROP_CONNECTED:
-		g_value_set_boolean (value, priv->connected);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
-
-	switch (prop_id) {
-	case PROP_PATH:
-		/* construct-only */
-		priv->path = g_value_dup_string (value);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez_device_init (NMBluezDevice *self)
-{
-}
-
-NMBluezDevice *
-nm_bluez_device_new (const char *path,
-                     const char *adapter_address,
-                     NMSettings *settings,
-                     int bluez_version)
-{
-	NMBluezDevice *self;
-	NMBluezDevicePrivate *priv;
-	const char *interface_name = NULL;
-
-	g_return_val_if_fail (path != NULL, 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,
-	                                       NM_BLUEZ_DEVICE_PATH, path,
-	                                       NULL);
-	if (!self)
-		return NULL;
-
-	nm_log_dbg (LOGD_BT, "bluez[%s] create NMBluezDevice", path);
-
-	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
-	priv->bluez_version = bluez_version;
-	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->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,
-	           (GAsyncReadyCallback) on_bus_acquired,
-	           g_object_ref (self));
-
-	switch (priv->bluez_version) {
-	case 4:
-		interface_name = NM_BLUEZ4_DEVICE_INTERFACE;
-		break;
-	case 5:
-		interface_name = NM_BLUEZ5_DEVICE_INTERFACE;
-		break;
-	}
-
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_NONE,
-	                          NULL,
-	                          NM_BLUEZ_SERVICE,
-	                          priv->path,
-	                          interface_name,
-	                          NULL,
-	                          (GAsyncReadyCallback) on_proxy_acquired,
-	                          g_object_ref (self));
-	return self;
-}
-
-static void
-dispose (GObject *object)
-{
-	NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	NMSettingsConnection *to_delete = NULL;
-
-	nm_clear_g_source (&priv->check_emit_usable_id);
-
-	if (priv->pan_connection) {
-		/* Check whether we want to remove the created connection. If so, we take a reference
-		 * and delete it at the end of dispose(). */
-		if (NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->pan_connection),
-		                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
-			to_delete = g_object_ref (priv->pan_connection);
-
-		priv->pan_connection = NULL;
-	}
-
-#if WITH_BLUEZ5_DUN
-	if (priv->b5_dun_context) {
-		nm_bluez5_dun_free (priv->b5_dun_context);
-		priv->b5_dun_context = NULL;
-	}
-#endif
-
-	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);
-	priv->connections = NULL;
-
-	if (priv->adapter5) {
-		g_signal_handlers_disconnect_by_func (priv->adapter5, adapter5_on_properties_changed, self);
-		g_clear_object (&priv->adapter5);
-	}
-
-	g_clear_object (&priv->dbus_connection);
-
-	G_OBJECT_CLASS (nm_bluez_device_parent_class)->dispose (object);
-
-	if (to_delete) {
-		nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path,
-		            nm_settings_connection_get_id (to_delete), nm_settings_connection_get_uuid (to_delete));
-		nm_settings_connection_delete (to_delete, FALSE);
-		g_object_unref (to_delete);
-	}
-
-	g_clear_object (&priv->settings);
-}
-
-static void
-finalize (GObject *object)
-{
-	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
-
-	nm_log_dbg (LOGD_BT, "bluez[%s]: finalize NMBluezDevice", priv->path);
-
-	g_free (priv->path);
-	g_free (priv->adapter_address);
-	g_free (priv->address);
-	g_free (priv->name);
-	g_free (priv->b4_iface);
-
-	if (priv->proxy)
-		g_signal_handlers_disconnect_by_data (priv->proxy, object);
-	g_clear_object (&priv->proxy);
-
-	G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object);
-}
-
-static void
-nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-
-	object_class->get_property = get_property;
-	object_class->set_property = set_property;
-	object_class->dispose = dispose;
-	object_class->finalize = finalize;
-
-	obj_properties[PROP_PATH] =
-	     g_param_spec_string (NM_BLUEZ_DEVICE_PATH, "", "",
-	                          NULL,
-	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-	                          G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_ADDRESS] =
-	     g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS, "", "",
-	                          NULL,
-	                          G_PARAM_READABLE |
-	                          G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_NAME] =
-	     g_param_spec_string (NM_BLUEZ_DEVICE_NAME, "", "",
-	                          NULL,
-	                          G_PARAM_READABLE |
-	                          G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_CAPABILITIES] =
-	     g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES, "", "",
-	                        0, G_MAXUINT, 0,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_USABLE] =
-	     g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE, "", "",
-	                           FALSE,
-	                           G_PARAM_READABLE |
-	                           G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_CONNECTED] =
-	     g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED, "", "",
-	                           FALSE,
-	                           G_PARAM_READABLE |
-	                           G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
-	signals[INITIALIZED] = g_signal_new (NM_BLUEZ_DEVICE_INITIALIZED,
-	                                     G_OBJECT_CLASS_TYPE (object_class),
-	                                     G_SIGNAL_RUN_LAST,
-	                                     0,
-	                                     NULL, NULL, NULL,
-	                                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
-	signals[REMOVED] =     g_signal_new (NM_BLUEZ_DEVICE_REMOVED,
-	                                     G_OBJECT_CLASS_TYPE (object_class),
-	                                     G_SIGNAL_RUN_LAST,
-	                                     0,
-	                                     NULL, NULL, NULL,
-	                                     G_TYPE_NONE, 0);
-}
-
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
deleted file mode 100644
index e8dd2ced..00000000
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2009 - 2014 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ_DEVICE_H__
-#define __NETWORKMANAGER_BLUEZ_DEVICE_H__
-
-#include "nm-connection.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))
-#define NM_BLUEZ_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
-#define NM_IS_BLUEZ_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE))
-#define NM_IS_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_DEVICE))
-#define NM_BLUEZ_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
-
-/* Properties */
-#define NM_BLUEZ_DEVICE_PATH         "path"
-#define NM_BLUEZ_DEVICE_ADDRESS      "address"
-#define NM_BLUEZ_DEVICE_NAME         "name"
-#define NM_BLUEZ_DEVICE_CAPABILITIES "capabilities"
-#define NM_BLUEZ_DEVICE_USABLE       "usable"
-#define NM_BLUEZ_DEVICE_CONNECTED    "connected"
-
-/* Signals */
-#define NM_BLUEZ_DEVICE_INITIALIZED  "initialized"
-#define NM_BLUEZ_DEVICE_REMOVED      "removed"
-
-typedef struct _NMBluezDevice NMBluezDevice;
-typedef struct _NMBluezDeviceClass NMBluezDeviceClass;
-
-GType nm_bluez_device_get_type (void);
-
-NMBluezDevice *nm_bluez_device_new (const char *path,
-                                    const char *adapter_address,
-                                    NMSettings *settings,
-                                    int bluez_version);
-
-const char *nm_bluez_device_get_path (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_initialized (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_usable (NMBluezDevice *self);
-
-const char *nm_bluez_device_get_address (NMBluezDevice *self);
-
-const char *nm_bluez_device_get_name (NMBluezDevice *self);
-
-guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_connected (NMBluezDevice *self);
-
-typedef void (*NMBluezDeviceConnectCallback) (NMBluezDevice *self,
-                                              const char *device,
-                                              GError *error,
-                                              gpointer user_data);
-
-void
-nm_bluez_device_connect_async (NMBluezDevice *self,
-                               NMBluetoothCapabilities connection_bt_type,
-                               GCancellable *cancellable,
-                               NMBluezDeviceConnectCallback callback,
-                               gpointer callback_user_data);
-
-void
-nm_bluez_device_disconnect (NMBluezDevice *self);
-
-#endif /* __NETWORKMANAGER_BLUEZ_DEVICE_H__ */
-
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index 7577ab8b..ef087d83 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -1,73 +1,185 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 - 2014 Red Hat, Inc.
  */
 
 #include "nm-default.h"
 
+#include "nm-bluez-manager.h"
+
 #include <signal.h>
 #include <stdlib.h>
 #include <gmodule.h>
 
+#include "nm-glib-aux/nm-dbus-aux.h"
+#include "nm-glib-aux/nm-c-list.h"
+#include "nm-dbus-manager.h"
 #include "devices/nm-device-factory.h"
 #include "devices/nm-device-bridge.h"
 #include "nm-setting-bluetooth.h"
 #include "settings/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-device-bt.h"
+#include "nm-manager.h"
+#include "nm-bluez5-dun.h"
 #include "nm-core-internal.h"
 #include "platform/nm-platform.h"
 #include "nm-std-aux/nm-dbus-compat.h"
 
 /*****************************************************************************/
 
-#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))
-#define NM_BLUEZ_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
-#define NM_IS_BLUEZ_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
-#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_BLUEZ_MANAGER))
-#define NM_BLUEZ_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+#if WITH_BLUEZ5_DUN
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_DUN
+#else
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_NONE
+#endif
+#define _NM_BT_CAPABILITY_SUPPORTED (NM_BT_CAPABILITY_NAP | _NM_BT_CAPABILITY_SUPPORTED_DUN)
+
+typedef struct {
+	const char *bdaddr;
+	CList lst_head;
+	NMBluetoothCapabilities bt_type:8;
+	char bdaddr_data[];
+} ConnDataHead;
+
+typedef struct {
+	NMSettingsConnection *sett_conn;
+	ConnDataHead *cdata_hd;
+	CList lst;
+} ConnDataElem;
 
 typedef struct {
-	int bluez_version;
+	GCancellable *ext_cancellable;
+	GCancellable *int_cancellable;
+	NMBtVTableRegisterCallback callback;
+	gpointer callback_user_data;
+	gulong ext_cancelled_id;
+} NetworkServerRegisterReqData;
+
+typedef struct {
+	GCancellable *ext_cancellable;
+	GCancellable *int_cancellable;
+	NMBluezManagerConnectCb callback;
+	gpointer callback_user_data;
+	char *device_name;
+	gulong ext_cancelled_id;
+	guint timeout_id;
+	guint timeout_wait_connect_id;
+} DeviceConnectReqData;
+
+typedef struct {
+	const char *object_path;
+
+	NMBluezManager *self;
 
+	/* Fields name with "d_" prefix are purely cached values from BlueZ's
+	 * ObjectManager D-Bus interface. There is no logic whatsoever about
+	 * them.
+	 */
+
+	CList process_change_lst;
+
+	struct {
+		char *address;
+	} d_adapter;
+
+	struct {
+		char *address;
+		char *name;
+		char *adapter;
+	} d_device;
+
+	struct {
+		char *interface;
+	} d_network;
+
+	struct {
+		CList lst;
+		char *adapter_address;
+		NMDevice *device_br;
+		NetworkServerRegisterReqData *r_req_data;
+	} x_network_server;
+
+	struct {
+		NMSettingsConnection *panu_connection;
+		NMDeviceBt *device_bt;
+		DeviceConnectReqData *c_req_data;
+		NMBluez5DunContext *connect_dun_context;
+		gulong device_bt_signal_id;
+	} x_device;
+
+	/* indicate whether the D-Bus object has the particular D-Bus interface. */
+	bool d_has_adapter_iface:1;
+	bool d_has_device_iface:1;
+	bool d_has_network_iface:1;
+	bool d_has_network_server_iface:1;
+
+	/* cached D-Bus properties for Device1 ("d_device*"). */
+	NMBluetoothCapabilities d_device_capabilities:6;
+	bool d_device_connected:1;
+	bool d_device_paired:1;
+
+	/* cached D-Bus properties for Network1 ("d_network*"). */
+	bool d_network_connected:1;
+
+	/* cached D-Bus properties for Adapter1 ("d_adapter*"). */
+	bool d_adapter_powered:1;
+
+	/* properties related to device ("x_device*"). */
+	NMBluetoothCapabilities x_device_connect_bt_type:6;
+	bool x_device_is_usable:1;
+	bool x_device_is_connected:1;
+
+	bool x_device_panu_connection_allow_create:1;
+
+	/* flag to remember last time when we checked wether the object
+	 * was  a suitable adapter that is usable to a device. */
+	bool was_usable_adapter_for_device_before:1;
+
+	char _object_path_intern[];
+} BzDBusObj;
+
+typedef struct {
+	NMManager *manager;
 	NMSettings *settings;
-	NMBluez4Manager *manager4;
-	NMBluez5Manager *manager5;
 
-	guint watch_name_id;
+	GDBusConnection *dbus_connection;
+
+	NMBtVTableNetworkServer vtable_network_server;
+
+	GCancellable *name_owner_get_cancellable;
+	GCancellable *get_managed_objects_cancellable;
+
+	GHashTable *bzobjs;
+
+	char *name_owner;
+
+	GHashTable *conn_data_heads;
+	GHashTable *conn_data_elems;
+
+	CList network_server_lst_head;
+
+	CList process_change_lst_head;
+
+	guint name_owner_changed_id;
 
-	GDBusProxy *introspect_proxy;
-	GCancellable *async_cancellable;
+	guint managed_objects_changed_id;
+
+	guint properties_changed_id;
+
+	guint process_change_idle_id;
+
+	bool settings_registered:1;
 } NMBluezManagerPrivate;
 
-typedef struct {
+struct _NMBluezManager {
 	NMDeviceFactory parent;
 	NMBluezManagerPrivate _priv;
-} NMBluezManager;
+};
 
-typedef struct {
+struct _NMBluezManagerClass {
 	NMDeviceFactoryClass parent;
-} NMBluezManagerClass;
-
-static GType nm_bluez_manager_get_type (void);
+};
 
 G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, NM_TYPE_DEVICE_FACTORY);
 
@@ -93,313 +205,2583 @@ nm_device_factory_create (GError **error)
 
 /*****************************************************************************/
 
-static void check_bluez_and_try_setup (NMBluezManager *self);
+static NMBluetoothCapabilities
+convert_uuids_to_capabilities (const char *const*strv)
+{
+	NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
+
+	if (strv) {
+		for (; strv[0]; strv++) {
+			gs_free char *s_part1 = NULL;
+			const char *str = strv[0];
+			const char *s;
+
+			s = strchr (str, '-');
+			if (!s)
+				continue;
+
+			s_part1 = g_strndup (str, s - str);
+			switch (g_ascii_strtoull (s_part1, NULL, 16)) {
+			case 0x1103:
+				capabilities |= NM_BT_CAPABILITY_DUN;
+				break;
+			case 0x1116:
+				capabilities |= NM_BT_CAPABILITY_NAP;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	return capabilities;
+}
+
+/*****************************************************************************/
+
+static void _cleanup_for_name_owner (NMBluezManager *self);
+static void _connect_disconnect (NMBluezManager *self,
+                                 BzDBusObj *bzobj,
+                                 const char *reason);
+static gboolean _bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
+                                                  gboolean require_powered);
+static gboolean _bzobjs_is_dead (const BzDBusObj *bzobj);
+static gboolean _bzobjs_device_is_usable (const BzDBusObj *bzobj,
+                                          BzDBusObj **out_adapter_bzobj,
+                                          gboolean *out_create_panu_connection);
+static gboolean _bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj);
+static ConnDataHead *_conn_track_find_head (NMBluezManager *self,
+                                            NMBluetoothCapabilities bt_type,
+                                            const char *bdaddr);
+static void _process_change_idle_schedule (NMBluezManager *self,
+                                           BzDBusObj *bzobj);
+static void _network_server_unregister_bridge (NMBluezManager *self,
+                                               BzDBusObj *bzobj,
+                                               const char *reason);
+static gboolean _connect_timeout_wait_connected_cb (gpointer user_data);
 
 /*****************************************************************************/
 
-struct AsyncData {
+static void
+_dbus_call_complete_cb_nop (GObject *source_object,
+                            GAsyncResult *res,
+                            gpointer user_data)
+{
+	/* we don't do anything at all. The only reason to register this
+	 * callback is so that GDBusConnection keeps the cancellable alive
+	 * long enough until the call completes.
+	 *
+	 * Note that this cancellable in turn is registered via
+	 * nm_shutdown_wait_obj_register_*(), to block shutdown until
+	 * we are done. */
+}
+
+/*****************************************************************************/
+
+static void
+_network_server_register_req_data_complete (NetworkServerRegisterReqData *r_req_data,
+                                            GError *error)
+{
+	nm_clear_g_signal_handler (r_req_data->ext_cancellable, &r_req_data->ext_cancelled_id);
+
+	nm_clear_g_cancellable (&r_req_data->int_cancellable);
+
+	if (r_req_data->callback) {
+		gs_free_error GError *error_cancelled = NULL;
+
+		if (g_cancellable_set_error_if_cancelled (r_req_data->ext_cancellable, &error_cancelled))
+			error = error_cancelled;
+
+		r_req_data->callback (error, r_req_data->callback_user_data);
+	}
+
+	g_object_unref (r_req_data->ext_cancellable);
+	nm_g_slice_free (r_req_data);
+}
+
+static void
+_device_connect_req_data_complete (DeviceConnectReqData *c_req_data,
+                                   NMBluezManager *self,
+                                   const char *device_name,
+                                   GError *error)
+{
+	nm_assert ((!!device_name) != (!!error));
+
+	nm_clear_g_signal_handler (c_req_data->ext_cancellable, &c_req_data->ext_cancelled_id);
+
+	nm_clear_g_cancellable (&c_req_data->int_cancellable);
+	nm_clear_g_source (&c_req_data->timeout_id);
+	nm_clear_g_source (&c_req_data->timeout_wait_connect_id);
+
+	if (c_req_data->callback) {
+		gs_free_error GError *error_cancelled = NULL;
+
+		if (g_cancellable_set_error_if_cancelled (c_req_data->ext_cancellable, &error_cancelled)) {
+			error = error_cancelled;
+			device_name = NULL;
+		}
+
+		c_req_data->callback (self, TRUE, device_name, error, c_req_data->callback_user_data);
+	}
+
+	g_object_unref (c_req_data->ext_cancellable);
+	nm_clear_g_free (&c_req_data->device_name);
+	nm_g_slice_free (c_req_data);
+}
+
+/*****************************************************************************/
+
+static BzDBusObj *
+_bz_dbus_obj_new (NMBluezManager *self,
+                  const char *object_path)
+{
+	BzDBusObj *bzobj;
+	gsize l;
+
+	nm_assert (NM_IS_BLUEZ_MANAGER (self));
+
+	l = strlen (object_path) + 1;
+
+	bzobj = g_malloc (sizeof (BzDBusObj) + l);
+	*bzobj = (BzDBusObj) {
+		.object_path                           = bzobj->_object_path_intern,
+		.self                                  = self,
+		.x_network_server.lst                  = C_LIST_INIT (bzobj->x_network_server.lst),
+		.process_change_lst                    = C_LIST_INIT (bzobj->process_change_lst),
+		.x_device_panu_connection_allow_create = TRUE,
+	};
+	memcpy (bzobj->_object_path_intern, object_path, l);
+
+	return bzobj;
+}
+
+static void
+_bz_dbus_obj_free (BzDBusObj *bzobj)
+{
+	nm_assert (bzobj);
+	nm_assert (NM_IS_BLUEZ_MANAGER (bzobj->self));
+	nm_assert (!bzobj->x_network_server.device_br);
+	nm_assert (!bzobj->x_network_server.r_req_data);
+	nm_assert (!bzobj->x_device.c_req_data);
+
+	c_list_unlink_stale (&bzobj->process_change_lst);
+	c_list_unlink_stale (&bzobj->x_network_server.lst);
+	g_free (bzobj->x_network_server.adapter_address);
+	g_free (bzobj->d_adapter.address);
+	g_free (bzobj->d_network.interface);
+	g_free (bzobj->d_device.address);
+	g_free (bzobj->d_device.name);
+	g_free (bzobj->d_device.adapter);
+	g_free (bzobj);
+}
+
+/*****************************************************************************/
+
+static const char *
+_bzobj_to_string (const BzDBusObj *bzobj, char *buf, gsize len)
+{
+	char *buf0 = buf;
+	const char *prefix = "";
+	gboolean device_is_usable;
+	gboolean create_panu_connection = FALSE;
+	gboolean network_server_is_usable;
+	char sbuf_cap[100];
+
+	if (len > 0)
+		buf[0] = '\0';
+
+	if (bzobj->d_has_adapter_iface) {
+		nm_utils_strbuf_append_str (&buf, &len, prefix);
+		prefix = ", ";
+		nm_utils_strbuf_append_str (&buf, &len, "Adapter1 {");
+		if (bzobj->d_adapter.address) {
+			nm_utils_strbuf_append (&buf, &len, " d.address: \"%s\"", bzobj->d_adapter.address);
+			if (bzobj->d_adapter_powered)
+				nm_utils_strbuf_append_str (&buf, &len, ",");
+		}
+		if (bzobj->d_adapter_powered)
+			nm_utils_strbuf_append (&buf, &len, " d.powered: 1");
+		nm_utils_strbuf_append_str (&buf, &len, " }");
+	}
+
+	if (bzobj->d_has_device_iface) {
+		const char *prefix1 = "";
+
+		nm_utils_strbuf_append_str (&buf, &len, prefix);
+		prefix = ", ";
+		nm_utils_strbuf_append_str (&buf, &len, "Device1 {");
+		if (bzobj->d_device.address) {
+			nm_utils_strbuf_append (&buf, &len, "%s d.address: \"%s\"", prefix1, bzobj->d_device.address);
+			prefix1 = ",";
+		}
+		if (bzobj->d_device.name) {
+			nm_utils_strbuf_append (&buf, &len, "%s d.name: \"%s\"", prefix1, bzobj->d_device.name);
+			prefix1 = ",";
+		}
+		if (bzobj->d_device.adapter) {
+			nm_utils_strbuf_append (&buf, &len, "%s d.adapter: \"%s\"", prefix1, bzobj->d_device.adapter);
+			prefix1 = ",";
+		}
+		if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+			nm_utils_strbuf_append (&buf, &len, "%s d.capabilities: \"%s\"",
+			                        prefix1,
+			                        nm_bluetooth_capability_to_string (bzobj->d_device_capabilities, sbuf_cap, sizeof (sbuf_cap)));
+			prefix1 = ",";
+		}
+		if (bzobj->d_device_connected) {
+			nm_utils_strbuf_append (&buf, &len, "%s d.connected: 1", prefix1);
+			prefix1 = ",";
+		}
+		if (bzobj->d_device_paired) {
+			nm_utils_strbuf_append (&buf, &len, "%s d.paired: 1", prefix1);
+			prefix1 = ",";
+		}
+		nm_utils_strbuf_append_str (&buf, &len, " }");
+	}
+
+	network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+	if (   bzobj->d_has_network_server_iface
+	    || network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst))
+	    || !c_list_is_empty (&bzobj->x_network_server.lst)
+	    || !nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)
+	    || bzobj->x_network_server.device_br
+	    || bzobj->x_network_server.r_req_data) {
+
+		nm_utils_strbuf_append_str (&buf, &len, prefix);
+		prefix = ", ";
+
+		nm_utils_strbuf_append (&buf, &len, "NetworkServer1 { ");
+
+		if (!bzobj->d_has_network_server_iface)
+			nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
+
+		if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
+			nm_utils_strbuf_append (&buf, &len, "usable: %d, used: %d", !!network_server_is_usable, !network_server_is_usable);
+		else if (network_server_is_usable)
+			nm_utils_strbuf_append (&buf, &len, "used: 1");
+		else
+			nm_utils_strbuf_append (&buf, &len, "usable: 0");
+
+		if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)) {
+			if (bzobj->x_network_server.adapter_address)
+				nm_utils_strbuf_append (&buf, &len, ", adapter-address: \"%s\"", bzobj->x_network_server.adapter_address);
+			else
+				nm_utils_strbuf_append (&buf, &len, ", adapter-address: <NULL>");
+		}
+
+		if (bzobj->x_network_server.device_br)
+			nm_utils_strbuf_append (&buf, &len, ", bridge-device: 1");
+
+		if (bzobj->x_network_server.r_req_data)
+			nm_utils_strbuf_append (&buf, &len, ", register-in-progress: 1");
+
+		nm_utils_strbuf_append_str (&buf, &len, " }");
+	}
+
+	device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
+
+	if (   bzobj->d_has_network_iface
+	    || bzobj->d_network.interface
+	    || bzobj->d_network_connected
+	    || create_panu_connection
+	    || bzobj->x_device.panu_connection
+	    || device_is_usable != bzobj->x_device_is_usable
+	    || bzobj->x_device.device_bt
+	    || bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
+	    || bzobj->x_device.connect_dun_context
+	    || bzobj->x_device.c_req_data
+	    || bzobj->x_device_is_connected != bzobj->d_network_connected) {
+
+		nm_utils_strbuf_append_str (&buf, &len, prefix);
+		prefix = ", ";
+		nm_utils_strbuf_append_str (&buf, &len, "Network1 {");
+		if (bzobj->d_network.interface)
+			nm_utils_strbuf_append (&buf, &len, " d.interface: \"%s\", ", bzobj->d_network.interface);
+		if (bzobj->d_network_connected)
+			nm_utils_strbuf_append (&buf, &len, " d.connected: %d, ", !!bzobj->d_network_connected);
+		if (!bzobj->d_has_network_iface)
+			nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
+		if (device_is_usable != bzobj->x_device_is_usable)
+			nm_utils_strbuf_append (&buf, &len, " usable: %d, used: %d", !!device_is_usable, !device_is_usable);
+		else if (device_is_usable)
+			nm_utils_strbuf_append (&buf, &len, " used: 1");
+		else
+			nm_utils_strbuf_append (&buf, &len, " usable: 0");
+
+		if (create_panu_connection)
+			nm_utils_strbuf_append (&buf, &len, ", create-panu-connection: 1");
+
+		if (bzobj->x_device.panu_connection)
+			nm_utils_strbuf_append (&buf, &len, ", has-panu-connection: 1");
+
+		if (bzobj->x_device.device_bt)
+			nm_utils_strbuf_append (&buf, &len, ", has-device: 1");
+
+		if (   bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
+		    || bzobj->x_device.connect_dun_context) {
+			nm_utils_strbuf_append (&buf, &len, ", connect: %s%s",
+			                        nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+			                        bzobj->x_device.connect_dun_context ? ",with-dun-context" : "");
+		}
+
+		if (bzobj->x_device.c_req_data)
+			nm_utils_strbuf_append (&buf, &len, ", connecting: 1");
+
+		if (bzobj->x_device_is_connected != bzobj->d_network_connected)
+			nm_utils_strbuf_append (&buf, &len, ", connected: %d", !!bzobj->x_device_is_connected);
+
+		nm_utils_strbuf_append_str (&buf, &len, " }");
+	}
+
+	if (_bzobjs_is_dead (bzobj)) {
+		nm_utils_strbuf_append_str (&buf, &len, prefix);
+		prefix = ", ";
+		nm_utils_strbuf_append_str (&buf, &len, "dead: 1");
+	}
+
+	if (!c_list_is_empty (&bzobj->process_change_lst)) {
+		nm_utils_strbuf_append_str (&buf, &len, prefix);
+		prefix = ", ";
+		nm_utils_strbuf_append (&buf, &len, "change-pending-on-idle: 1");
+	}
+
+	if (_bzobjs_adapter_is_usable_for_device (bzobj) != bzobj->was_usable_adapter_for_device_before) {
+		nm_utils_strbuf_append_str (&buf, &len, prefix);
+		prefix = ", ";
+		nm_utils_strbuf_append (&buf, &len, "change-usable-adapter-for-device: 1");
+	}
+
+	return buf0;
+}
+
+#define _LOG_bzobj(bzobj, context) \
+	G_STMT_START { \
+		const BzDBusObj *const _bzobj = (bzobj); \
+		char _buf[500]; \
+		\
+		_LOGT ("change %-21s %s : { %s }", \
+		       (context), \
+		       _bzobj->object_path, \
+		       _bzobj_to_string (_bzobj, _buf, sizeof (_buf))); \
+	} G_STMT_END
+
+static gboolean
+_bzobjs_is_dead (const BzDBusObj *bzobj)
+{
+	return    !bzobj->d_has_adapter_iface
+	       && !bzobj->d_has_device_iface
+	       && !bzobj->d_has_network_iface
+	       && !bzobj->d_has_network_server_iface
+	       && c_list_is_empty (&bzobj->process_change_lst);
+}
+
+static BzDBusObj *
+_bzobjs_get (NMBluezManager *self, const char *object_path)
+{
+	return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->bzobjs, &object_path);
+}
+
+static BzDBusObj *
+_bzobjs_add (NMBluezManager *self,
+             const char *object_path)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	BzDBusObj *bzobj;
+
+	bzobj = _bz_dbus_obj_new (self, object_path);
+	if (!g_hash_table_add (priv->bzobjs, bzobj))
+		nm_assert_not_reached ();
+	return bzobj;
+}
+
+static void
+_bzobjs_del (BzDBusObj *bzobj)
+{
+	nm_assert (bzobj);
+	nm_assert (bzobj == _bzobjs_get (bzobj->self, bzobj->object_path));
+
+	if (!g_hash_table_remove (NM_BLUEZ_MANAGER_GET_PRIVATE (bzobj->self)->bzobjs, bzobj))
+		nm_assert_not_reached ();
+}
+
+static void
+_bzobjs_del_if_dead (BzDBusObj *bzobj)
+{
+	if (_bzobjs_is_dead (bzobj))
+		_bzobjs_del (bzobj);
+}
+
+static BzDBusObj *
+_bzobjs_init (NMBluezManager *self, BzDBusObj **inout, const char *object_path)
+{
+	nm_assert (NM_IS_BLUEZ_MANAGER (self));
+	nm_assert (object_path);
+	nm_assert (inout);
+
+	if (!*inout) {
+		*inout = _bzobjs_get (self, object_path);
+		if (!*inout)
+			*inout = _bzobjs_add (self, object_path);
+	}
+
+	nm_assert (nm_streq ((*inout)->object_path, object_path));
+	nm_assert (*inout == _bzobjs_get (self, object_path));
+	return *inout;
+}
+
+static gboolean
+_bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj)
+{
+	return    bzobj->d_has_adapter_iface
+	       && bzobj->d_adapter.address
+	       && bzobj->d_adapter_powered;
+}
+
+static gboolean
+_bzobjs_device_is_usable (const BzDBusObj *bzobj,
+                          BzDBusObj **out_adapter_bzobj,
+                          gboolean *out_create_panu_connection)
+{
 	NMBluezManager *self;
-	GCancellable *async_cancellable;
-};
+	NMBluezManagerPrivate *priv;
+	gboolean usable_dun = FALSE;
+	gboolean usable_nap = FALSE;
+	BzDBusObj *bzobj_adapter;
+	gboolean create_panu_connection = FALSE;
+
+	if (   !bzobj->d_has_device_iface
+	    || !NM_FLAGS_ANY ((NMBluetoothCapabilities) bzobj->d_device_capabilities, _NM_BT_CAPABILITY_SUPPORTED)
+	    || !bzobj->d_device.name
+	    || !bzobj->d_device.address
+	    || !bzobj->d_device_paired
+	    || !bzobj->d_device.adapter)
+		goto out_unusable;
+
+	self = bzobj->self;
+
+	priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	if (!priv->settings_registered)
+		goto out_unusable;
+
+	bzobj_adapter = _bzobjs_get (self, bzobj->d_device.adapter);
+	if (   !bzobj_adapter
+	    || !_bzobjs_adapter_is_usable_for_device (bzobj_adapter))
+		goto out_unusable;
+
+#if WITH_BLUEZ5_DUN
+	if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_DUN)) {
+		if (_conn_track_find_head (self, NM_BT_CAPABILITY_DUN, bzobj->d_device.address))
+			usable_dun = TRUE;
+	}
+#endif
+
+	if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)) {
+		if (!bzobj->d_has_network_iface)
+			usable_nap = FALSE;
+		else if (_conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address))
+			usable_nap = TRUE;
+		else if (bzobj->x_device_panu_connection_allow_create) {
+			/* We didn't yet try to create a connection. Presume we are going to create
+			 * it when the time comes... */
+			usable_nap = TRUE;
+			create_panu_connection = TRUE;
+		}
+	}
+
+	if (   !usable_dun
+	    && !usable_nap) {
+		if (   bzobj->x_device.device_bt
+		    && nm_device_get_state (NM_DEVICE (bzobj->x_device.device_bt)) > NM_DEVICE_STATE_DISCONNECTED) {
+			/* The device is still activated... the absence of a profile does not
+			 * render it unusable (yet). But since there is no more profile, the
+			 * device is probably about to disconnect. */
+		} else
+			goto out_unusable;
+	}
+
+	NM_SET_OUT (out_create_panu_connection, create_panu_connection);
+	NM_SET_OUT (out_adapter_bzobj, bzobj_adapter);
+	return TRUE;
 
-static struct AsyncData *
-async_data_pack (NMBluezManager *self)
+out_unusable:
+	NM_SET_OUT (out_create_panu_connection, FALSE);
+	NM_SET_OUT (out_adapter_bzobj, NULL);
+	return FALSE;
+}
+
+static gboolean
+_bzobjs_device_is_connected (const BzDBusObj *bzobj)
 {
-	struct AsyncData *data = g_new (struct AsyncData, 1);
+	nm_assert (_bzobjs_device_is_usable (bzobj, NULL, NULL));
+
+	if (   !bzobj->d_has_device_iface
+	    || !bzobj->d_device_connected)
+		return FALSE;
+
+	if (   bzobj->d_has_network_iface
+	    && bzobj->d_network_connected)
+		return TRUE;
+	if (bzobj->x_device.connect_dun_context) {
+		/* As long as we have a dun-context, we consider it connected.
+		 *
+		 * We require NMDeviceBt to try to connect to the modem, and if that fails,
+		 * it will disconnect. */
+		return TRUE;
+	}
+	return FALSE;
+}
 
-	data->self = self;
-	data->async_cancellable = g_object_ref (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->async_cancellable);
-	return data;
+static gboolean
+_bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
+                                  gboolean require_powered)
+{
+	return    bzobj->d_has_network_server_iface
+	       && bzobj->d_has_adapter_iface
+	       && bzobj->d_adapter.address
+	       && (   !require_powered
+	           || bzobj->d_adapter_powered);
 }
 
-static NMBluezManager *
-async_data_unpack (struct AsyncData *async_data)
+/*****************************************************************************/
+
+static ConnDataHead *
+_conn_data_head_new (NMBluetoothCapabilities bt_type,
+                     const char *bdaddr)
 {
-	NMBluezManager *self = g_cancellable_is_cancelled (async_data->async_cancellable)
-	                       ? NULL : async_data->self;
+	ConnDataHead *cdata_hd;
+	gsize l;
 
-	g_object_unref (async_data->async_cancellable);
-	g_free (async_data);
-	return self;
+	nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN,
+	                               NM_BT_CAPABILITY_NAP));
+	nm_assert (bdaddr);
+
+	l = strlen (bdaddr) + 1;
+	cdata_hd = g_malloc (sizeof (ConnDataHead) + l);
+	*cdata_hd = (ConnDataHead) {
+		.bdaddr   = cdata_hd->bdaddr_data,
+		.lst_head = C_LIST_INIT (cdata_hd->lst_head),
+		.bt_type  = bt_type,
+	};
+	memcpy (cdata_hd->bdaddr_data, bdaddr, l);
+
+	nm_assert (cdata_hd->bt_type == bt_type);
+
+	return cdata_hd;
+}
+
+static guint
+_conn_data_head_hash (gconstpointer ptr)
+{
+	const ConnDataHead *cdata_hd = ptr;
+	NMHashState h;
+
+	nm_hash_init (&h, 520317467u);
+	nm_hash_update_val (&h, (NMBluetoothCapabilities) cdata_hd->bt_type);
+	nm_hash_update_str (&h, cdata_hd->bdaddr);
+	return nm_hash_complete (&h);
+}
+
+static gboolean
+_conn_data_head_equal (gconstpointer a, gconstpointer b)
+{
+	const ConnDataHead *cdata_hd_a = a;
+	const ConnDataHead *cdata_hd_b = b;
+
+	return     cdata_hd_a->bt_type == cdata_hd_b->bt_type
+	        && nm_streq (cdata_hd_a->bdaddr, cdata_hd_b->bdaddr);
+}
+
+static ConnDataHead *
+_conn_track_find_head (NMBluezManager *self,
+                       NMBluetoothCapabilities bt_type,
+                       const char *bdaddr)
+{
+	ConnDataHead cdata_hd = {
+		.bt_type = bt_type,
+		.bdaddr  = bdaddr,
+	};
+
+	return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_heads, &cdata_hd);
+}
+
+static ConnDataElem *
+_conn_track_find_elem (NMBluezManager *self,
+                       NMSettingsConnection *sett_conn)
+{
+	G_STATIC_ASSERT (G_STRUCT_OFFSET (ConnDataElem, sett_conn) == 0);
+
+	return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_elems, &sett_conn);
+}
+
+static gboolean
+_conn_track_is_relevant_connection (NMConnection *connection,
+                                    NMBluetoothCapabilities *out_bt_type,
+                                    const char **out_bdaddr)
+{
+	NMSettingBluetooth *s_bt;
+	NMBluetoothCapabilities bt_type;
+	const char *bdaddr;
+	const char *b_type;
+
+	s_bt = nm_connection_get_setting_bluetooth (connection);
+	if (!s_bt)
+		return FALSE;
+
+	if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
+		return FALSE;
+
+	bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
+	if (!bdaddr)
+		return FALSE;
+
+	b_type = nm_setting_bluetooth_get_connection_type (s_bt);
+
+	if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+		bt_type = NM_BT_CAPABILITY_DUN;
+	else if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+		bt_type = NM_BT_CAPABILITY_NAP;
+	else
+		return FALSE;
+
+	NM_SET_OUT (out_bt_type, bt_type);
+	NM_SET_OUT (out_bdaddr, bdaddr);
+	return TRUE;
+}
+
+static gboolean
+_conn_track_is_relevant_sett_conn (NMSettingsConnection *sett_conn,
+                                   NMBluetoothCapabilities *out_bt_type,
+                                   const char **out_bdaddr)
+{
+	NMConnection *connection;
+
+	connection = nm_settings_connection_get_connection (sett_conn);
+	if (!connection)
+		return FALSE;
+
+	return _conn_track_is_relevant_connection (connection, out_bt_type, out_bdaddr);
+}
+
+static gboolean
+_conn_track_is_relevant_for_sett_conn (NMSettingsConnection *sett_conn,
+                                       NMBluetoothCapabilities bt_type,
+                                       const char *bdaddr)
+{
+	NMBluetoothCapabilities x_bt_type;
+	const char *x_bdaddr;
+
+	return    bdaddr
+	       && _conn_track_is_relevant_sett_conn (sett_conn, &x_bt_type, &x_bdaddr)
+	       && x_bt_type == bt_type
+	       && nm_streq (x_bdaddr, bdaddr);
+}
+
+static void
+_conn_track_schedule_notify (NMBluezManager *self,
+                             NMBluetoothCapabilities bt_type,
+                             const char *bdaddr)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	GHashTableIter iter;
+	BzDBusObj *bzobj;
+
+	g_hash_table_iter_init (&iter, priv->bzobjs);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+		gboolean device_is_usable;
+
+		device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+		if (bzobj->x_device_is_usable != device_is_usable)
+			_process_change_idle_schedule (self, bzobj);
+	}
 }
 
-/**
- * Cancel any current attempt to detect the version and cleanup
- * the related fields.
- **/
 static void
-cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name)
+_conn_track_update (NMBluezManager *self,
+                    NMSettingsConnection *sett_conn,
+                    gboolean track,
+                    gboolean *out_changed,
+                    gboolean *out_changed_usable,
+                    ConnDataElem **out_conn_data_elem)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	ConnDataHead *cdata_hd;
+	ConnDataElem *cdata_el;
+	ConnDataElem *cdata_el_remove = NULL;
+	NMBluetoothCapabilities bt_type;
+	const char *bdaddr;
+	gboolean changed = FALSE;
+	gboolean changed_usable = FALSE;
+	char sbuf_cap[100];
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+
+	cdata_el = _conn_track_find_elem (self, sett_conn);
+
+	if (track)
+		track = _conn_track_is_relevant_sett_conn (sett_conn, &bt_type, &bdaddr);
 
-	nm_clear_g_cancellable (&priv->async_cancellable);
+	if (!track) {
+		cdata_el_remove = g_steal_pointer (&cdata_el);
+		goto out_remove;
+	}
+
+	if (cdata_el) {
+		cdata_hd = cdata_el->cdata_hd;
+		if (   cdata_hd->bt_type != bt_type
+	        || !nm_streq (cdata_hd->bdaddr, bdaddr))
+			cdata_el_remove = g_steal_pointer (&cdata_el);
+	}
+
+	if (!cdata_el) {
+		_LOGT ("connecton: track for %s, %s: %s (%s)",
+		       nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
+		       bdaddr,
+		       nm_settings_connection_get_uuid (sett_conn),
+		       nm_settings_connection_get_id (sett_conn));
+		changed = TRUE;
+		cdata_hd = _conn_track_find_head (self, bt_type, bdaddr);
+		if (!cdata_hd) {
+			changed_usable = TRUE;
+			cdata_hd = _conn_data_head_new (bt_type, bdaddr);
+			if (!g_hash_table_add (priv->conn_data_heads, cdata_hd))
+				nm_assert_not_reached ();
+			_conn_track_schedule_notify (self, bt_type, bdaddr);
+		}
+		cdata_el = g_slice_new (ConnDataElem);
+		cdata_el->sett_conn = sett_conn;
+		cdata_el->cdata_hd = cdata_hd;
+		c_list_link_tail (&cdata_hd->lst_head, &cdata_el->lst);
+		if (!g_hash_table_add (priv->conn_data_elems, cdata_el))
+			nm_assert_not_reached ();
+	}
 
-	g_clear_object (&priv->introspect_proxy);
+out_remove:
+	if (cdata_el_remove) {
+		GHashTableIter iter;
+		BzDBusObj *bzobj;
+
+		_LOGT ("connecton: untrack for %s, %s: %s (%s)",
+		       nm_bluetooth_capability_to_string (cdata_el_remove->cdata_hd->bt_type, sbuf_cap, sizeof (sbuf_cap)),
+		       cdata_el_remove->cdata_hd->bdaddr,
+		       nm_settings_connection_get_uuid (sett_conn),
+		       nm_settings_connection_get_id (sett_conn));
+
+		g_hash_table_iter_init (&iter, priv->bzobjs);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+			if (bzobj->x_device.panu_connection == sett_conn)
+				bzobj->x_device.panu_connection = NULL;
+		}
 
-	if (do_unwatch_name && priv->watch_name_id) {
-		g_bus_unwatch_name (priv->watch_name_id);
-		priv->watch_name_id = 0;
+		changed = TRUE;
+		cdata_hd = cdata_el_remove->cdata_hd;
+		c_list_unlink_stale (&cdata_el_remove->lst);
+		if (!g_hash_table_remove (priv->conn_data_elems, cdata_el_remove))
+			nm_assert_not_reached ();
+		if (c_list_is_empty (&cdata_hd->lst_head)) {
+			changed_usable = TRUE;
+			_conn_track_schedule_notify (self, cdata_hd->bt_type, cdata_hd->bdaddr);
+			if (!g_hash_table_remove (priv->conn_data_heads, cdata_hd))
+				nm_assert_not_reached ();
+		}
 	}
+
+	NM_SET_OUT (out_changed, changed);
+	NM_SET_OUT (out_changed_usable, changed_usable);
+	NM_SET_OUT (out_conn_data_elem, cdata_el);
+}
+
+/*****************************************************************************/
+
+static void
+cp_connection_added (NMSettings *settings,
+                     NMSettingsConnection *sett_conn,
+                     NMBluezManager *self)
+{
+	_conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
 }
 
 static void
-manager_bdaddr_added_cb (GObject *manager,
-                         NMBluezDevice *bt_device,
-                         const char *bdaddr,
-                         const char *name,
-                         const char *object_path,
-                         guint32 capabilities,
-                         gpointer user_data)
+cp_connection_updated (NMSettings *settings,
+                       NMSettingsConnection *sett_conn,
+                       guint update_reason_u,
+                       NMBluezManager *self)
 {
-	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
-	NMDevice *device;
-	gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN);
-	gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP);
+	_conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
+}
 
-	g_return_if_fail (bdaddr != NULL);
-	g_return_if_fail (name != NULL);
-	g_return_if_fail (object_path != NULL);
-	g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE);
-	g_return_if_fail (NM_IS_BLUEZ_DEVICE (bt_device));
+static void
+cp_connection_removed (NMSettings *settings,
+                       NMSettingsConnection *sett_conn,
+                       NMBluezManager *self)
+{
+	_conn_track_update (self, sett_conn, FALSE, NULL, NULL, NULL);
+}
+
+/*****************************************************************************/
 
-	device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities);
-	if (!device)
+static NMBluezManager *
+_network_server_get_bluez_manager (const NMBtVTableNetworkServer *vtable_network_server)
+{
+	NMBluezManager *self;
+
+	self = (NMBluezManager *) (((char *) vtable_network_server) - G_STRUCT_OFFSET (NMBluezManager, _priv.vtable_network_server));
+
+	g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), NULL);
+
+	return self;
+}
+
+static BzDBusObj *
+_network_server_find_has_device (NMBluezManagerPrivate *priv,
+                                 NMDevice *device)
+{
+	BzDBusObj *bzobj;
+
+	c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
+		if (bzobj->x_network_server.device_br == device)
+			return bzobj;
+	}
+	return NULL;
+}
+
+static BzDBusObj *
+_network_server_find_available (NMBluezManagerPrivate *priv,
+                                const char *addr,
+                                NMDevice *device_accept_busy)
+{
+	BzDBusObj *bzobj;
+
+	c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
+		if (bzobj->x_network_server.device_br) {
+			if (bzobj->x_network_server.device_br != device_accept_busy)
+				continue;
+		}
+		if (   addr
+		    && !nm_streq (addr, bzobj->d_adapter.address))
+			continue;
+		nm_assert (!bzobj->x_network_server.r_req_data);
+		return bzobj;
+	}
+	return NULL;
+}
+
+static gboolean
+_network_server_vt_is_available (const NMBtVTableNetworkServer *vtable,
+                                 const char *addr,
+                                 NMDevice *device_accept_busy)
+{
+	NMBluezManager *self = _network_server_get_bluez_manager (vtable);
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	return !!_network_server_find_available (priv, addr, device_accept_busy);
+}
+
+static void
+_network_server_register_cb (GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer user_data)
+{
+	gs_unref_variant GVariant *ret = NULL;
+	gs_free_error GError *error = NULL;
+	BzDBusObj *bzobj;
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+	if (   !ret
+	    && nm_utils_error_is_cancelled (error, FALSE))
 		return;
 
-	_LOGI ("BT device %s (%s) added (%s%s%s)",
-	       name,
-	       bdaddr,
-	       has_dun ? "DUN" : "",
-	       has_dun && has_nap ? " " : "",
-	       has_nap ? "NAP" : "");
-	g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
-	g_object_unref (device);
+	bzobj = user_data;
+
+	if (!ret) {
+		_LOGT ("NAP: [%s]: registering failed: %s", bzobj->object_path, error->message);
+	} else
+		_LOGT ("NAP: [%s]: registration successful", bzobj->object_path);
+
+	g_clear_object (&bzobj->x_network_server.r_req_data->int_cancellable);
+	_network_server_register_req_data_complete (g_steal_pointer (&bzobj->x_network_server.r_req_data), error);
+}
+
+static void
+_network_server_register_cancelled_cb (GCancellable *cancellable,
+                                       BzDBusObj *bzobj)
+{
+	_network_server_unregister_bridge (bzobj->self, bzobj, "registration cancelled");
+}
+
+static gboolean
+_network_server_vt_register_bridge (const NMBtVTableNetworkServer *vtable,
+                                    const char *addr,
+                                    NMDevice *device,
+                                    GCancellable *cancellable,
+                                    NMBtVTableRegisterCallback callback,
+                                    gpointer callback_user_data,
+                                    GError **error)
+{
+	NMBluezManager *self = _network_server_get_bluez_manager (vtable);
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	NetworkServerRegisterReqData *r_req_data;
+	BzDBusObj *bzobj;
+	const char *ifname;
+
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
+
+	nm_assert (!g_cancellable_is_cancelled (cancellable));
+	nm_assert (!_network_server_find_has_device (priv, device));
+
+	ifname = nm_device_get_iface (device);
+	g_return_val_if_fail (ifname, FALSE);
+
+	g_return_val_if_fail (ifname, FALSE);
+
+	bzobj = _network_server_find_available (priv, addr, NULL);
+	if (!bzobj) {
+		/* The device checked that a network server is available, before
+		 * starting the activation, but for some reason it no longer is.
+		 * Indicate that the activation should not proceed. */
+		if (addr) {
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    "adapter %s is not available for %s",
+			                    addr, ifname);
+		} else {
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    "no adapter available for %s",
+			                    ifname);
+		}
+		return FALSE;
+	}
+
+	_LOGD ("NAP: [%s]: registering \"%s\" on adapter %s",
+	       bzobj->object_path,
+	       ifname,
+	       bzobj->d_adapter.address);
+
+	r_req_data = g_slice_new (NetworkServerRegisterReqData);
+	*r_req_data = (NetworkServerRegisterReqData) {
+		.int_cancellable     = g_cancellable_new (),
+		.ext_cancellable     = g_object_ref (cancellable),
+		.callback            = callback,
+		.callback_user_data  = callback_user_data,
+		.ext_cancelled_id    = g_signal_connect (cancellable,
+		                                         "cancelled",
+		                                         G_CALLBACK (_network_server_register_cancelled_cb),
+		                                         bzobj),
+	};
+
+	bzobj->x_network_server.device_br = g_object_ref (device);
+	bzobj->x_network_server.r_req_data = r_req_data;
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        priv->name_owner,
+	                        bzobj->object_path,
+	                        NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+	                        "Register",
+	                        g_variant_new ("(ss)",
+	                                       BLUETOOTH_CONNECT_NAP,
+	                                       ifname),
+	                        NULL,
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        -1,
+	                        bzobj->x_network_server.r_req_data->int_cancellable,
+	                        _network_server_register_cb,
+	                        bzobj);
+	return TRUE;
+}
+
+static void
+_network_server_unregister_bridge_complete_on_idle_cb (gpointer user_data,
+                                                       GCancellable *cancellable)
+{
+	gs_free_error GError *error = NULL;
+	gs_free char *reason  = NULL;
+	NetworkServerRegisterReqData *r_req_data;
+
+	nm_utils_user_data_unpack (user_data, &r_req_data, &reason);
+
+	nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
+	                    "registration was aborted due to %s",
+	                    reason);
+	_network_server_register_req_data_complete (r_req_data, error);
 }
 
 static void
-manager_network_server_added_cb (GObject *manager,
-                                 gpointer user_data)
+_network_server_unregister_bridge (NMBluezManager *self,
+                                   BzDBusObj *bzobj,
+                                   const char *reason)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	_nm_unused gs_unref_object NMDevice *device = NULL;
+	NetworkServerRegisterReqData *r_req_data;
+
+	nm_assert (NM_IS_DEVICE (bzobj->x_network_server.device_br));
+
+	_LOGD ("NAP: [%s]: unregistering \"%s\" (%s)",
+	       bzobj->object_path,
+	       nm_device_get_iface (bzobj->x_network_server.device_br),
+	       reason);
+
+	device = g_steal_pointer (&bzobj->x_network_server.device_br);
+
+	r_req_data = g_steal_pointer (&bzobj->x_network_server.r_req_data);
+
+	if (priv->name_owner) {
+		gs_unref_object GCancellable *cancellable = NULL;
+
+		cancellable = g_cancellable_new ();
+
+		nm_shutdown_wait_obj_register_cancellable_full (cancellable,
+		                                                g_strdup_printf ("bt-unregister-nap[%s]", bzobj->object_path),
+		                                                TRUE);
+
+		g_dbus_connection_call (priv->dbus_connection,
+		                        priv->name_owner,
+		                        bzobj->object_path,
+		                        NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+		                        "Unregister",
+		                        g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
+		                        NULL,
+		                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                        -1,
+		                        cancellable,
+		                        _dbus_call_complete_cb_nop,
+		                        NULL);
+	}
+
+	if (r_req_data) {
+		nm_clear_g_cancellable (&r_req_data->int_cancellable);
+		nm_utils_invoke_on_idle (_network_server_unregister_bridge_complete_on_idle_cb,
+		                         nm_utils_user_data_pack (r_req_data, g_strdup (reason)),
+		                         r_req_data->ext_cancellable);
+	}
+
+	_nm_device_bridge_notify_unregister_bt_nap (device, reason);
+}
+
+static gboolean
+_network_server_vt_unregister_bridge (const NMBtVTableNetworkServer *vtable,
+                                      NMDevice *device)
 {
-	nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (user_data), NULL);
+	NMBluezManager *self = _network_server_get_bluez_manager (vtable);
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	BzDBusObj *bzobj;
+
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+	bzobj = _network_server_find_has_device (priv, device);
+	if (bzobj)
+		_network_server_unregister_bridge (self, bzobj, "disconnecting");
+
+	return TRUE;
 }
 
 static void
-setup_version_number (NMBluezManager *self, int bluez_version)
+_network_server_process_change (BzDBusObj *bzobj,
+                                gboolean *out_emit_device_availability_changed)
 {
+	NMBluezManager *self = bzobj->self;
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	gboolean network_server_is_usable;
+	gboolean emit_device_availability_changed = FALSE;
+
+	network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
 
-	g_return_if_fail (!priv->bluez_version);
+	if (!network_server_is_usable) {
 
-	_LOGI ("use BlueZ version %d", bluez_version);
+		if (!c_list_is_empty (&bzobj->x_network_server.lst)) {
+			emit_device_availability_changed = TRUE;
+			c_list_unlink (&bzobj->x_network_server.lst);
+		}
 
-	priv->bluez_version = bluez_version;
+		nm_clear_g_free (&bzobj->x_network_server.adapter_address);
+
+		if (bzobj->x_network_server.device_br) {
+			_network_server_unregister_bridge (self,
+			                                   bzobj,
+			                                     _bzobjs_network_server_is_usable (bzobj, FALSE)
+			                                   ? "adapter disabled"
+			                                   : "adapter disappeared");
+		}
+
+	} else {
+
+		if (!nm_streq0 (bzobj->x_network_server.adapter_address, bzobj->d_adapter.address)) {
+			emit_device_availability_changed = TRUE;
+			g_free (bzobj->x_network_server.adapter_address);
+			bzobj->x_network_server.adapter_address = g_strdup (bzobj->d_adapter.address);
+		}
 
-	/* Just detected the version. Cleanup the ongoing checking/detection. */
-	cleanup_checking (self, TRUE);
+		if (c_list_is_empty (&bzobj->x_network_server.lst)) {
+			emit_device_availability_changed = TRUE;
+			c_list_link_tail (&priv->network_server_lst_head, &bzobj->x_network_server.lst);
+		}
+
+	}
+
+	if (emit_device_availability_changed)
+		NM_SET_OUT (out_emit_device_availability_changed, TRUE);
 }
 
+/*****************************************************************************/
+
 static void
-setup_bluez4 (NMBluezManager *self)
+_conn_create_panu_connection (NMBluezManager *self,
+                              BzDBusObj *bzobj)
 {
-	NMBluez4Manager *manager;
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingsConnection *added;
+	NMSetting *setting;
+	gs_free char *id = NULL;
+	char uuid[37];
+	gs_free_error GError *error = NULL;
+
+	nm_utils_uuid_generate_buf (uuid);
+	id = g_strdup_printf (_("%s Network"), bzobj->d_device.name);
+
+	connection = nm_simple_connection_new ();
+
+	setting = nm_setting_connection_new ();
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	setting = nm_setting_bluetooth_new ();
+	g_object_set (setting,
+	              NM_SETTING_BLUETOOTH_BDADDR, bzobj->d_device.address,
+	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
+		_LOGE ("connection: couldn't generate a connection for NAP device: %s",
+		       error->message);
+		g_return_if_reached ();
+	}
 
-	g_return_if_fail (!priv->manager4 && !priv->manager5 && !priv->bluez_version);
+	nm_assert (_conn_track_is_relevant_connection (connection, NULL, NULL));
+
+	_LOGT ("connection: create in-memory PANU connection %s (%s) for device \"%s\" (%s)",
+	       uuid,
+	       id,
+	       bzobj->d_device.name,
+	       bzobj->d_device.address);
+
+	nm_settings_add_connection (priv->settings,
+	                            connection,
+	                            NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+	                            NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
+	                            NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+	                            &added,
+	                            &error);
+	if (!added) {
+		_LOGW ("connection: couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
+		       id, uuid, error->message);
+		return;
+	}
 
-	setup_version_number (self, 4);
-	priv->manager4 = manager = nm_bluez4_manager_new (priv->settings);
+	if (   !_conn_track_is_relevant_for_sett_conn (added, NM_BT_CAPABILITY_NAP, bzobj->d_device.address)
+	    || !_conn_track_find_elem (self, added)
+	    || bzobj->x_device.panu_connection) {
+		_LOGE ("connection: something went wrong creating PANU connection %s (%s) for device '%s'",
+		       uuid, id, bzobj->d_device.address);
+		g_return_if_reached ();
+	}
 
-	g_signal_connect (manager,
-	                  NM_BLUEZ_MANAGER_BDADDR_ADDED,
-	                  G_CALLBACK (manager_bdaddr_added_cb),
-	                  self);
+	bzobj->x_device.panu_connection = added;
+}
+
+/*****************************************************************************/
+
+static void
+_device_state_changed_cb (NMDevice *device,
+                          guint new_state_u,
+                          guint old_state_u,
+                          guint reason_u,
+                          gpointer user_data)
+{
+	BzDBusObj *bzobj = user_data;
 
-	nm_bluez4_manager_query_devices (manager);
+	if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
+		/* the device got unusable? Need to revisit it... */
+		_process_change_idle_schedule (bzobj->self, bzobj);
+	}
 }
 
 static void
-setup_bluez5 (NMBluezManager *self)
+_device_process_change (BzDBusObj *bzobj)
+{
+	NMBluezManager *self = bzobj->self;
+	gs_unref_object NMDeviceBt *device_added = NULL;
+	gs_unref_object NMDeviceBt *device_deleted = NULL;
+	gboolean device_is_usable;
+	gboolean create_panu_connection = FALSE;
+
+	device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
+
+	if (create_panu_connection) {
+		bzobj->x_device_panu_connection_allow_create = FALSE;
+		_conn_create_panu_connection (self, bzobj);
+		device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+	} else {
+		if (   device_is_usable
+		    && bzobj->x_device_panu_connection_allow_create
+		    && NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)
+		    && _conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address) ) {
+			/* We have a useable device and also a panu-connection. We block future attemps
+			 * to generate a connection. */
+			bzobj->x_device_panu_connection_allow_create = FALSE;
+		}
+		if (bzobj->x_device.panu_connection) {
+			if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (bzobj->x_device.panu_connection),
+			                   NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
+				/* the connection that we generated earlier still exists, but it's not longer the same
+				 * as it was when we created it. Forget about it, so that we don't delete the profile later... */
+				bzobj->x_device.panu_connection = NULL;
+			} else {
+				if (   !device_is_usable
+				    || !_conn_track_is_relevant_for_sett_conn (bzobj->x_device.panu_connection,
+				                                               NM_BT_CAPABILITY_NAP,
+				                                               bzobj->d_device.address)) {
+					_LOGT ("connection: delete in-memory PANU connection %s (%s) as device %s",
+					       nm_settings_connection_get_uuid (bzobj->x_device.panu_connection),
+					       nm_settings_connection_get_id (bzobj->x_device.panu_connection),
+					       !device_is_usable ? "is now unusable" : "no longer matches");
+					bzobj->x_device_panu_connection_allow_create = TRUE;
+					nm_settings_connection_delete (g_steal_pointer (&bzobj->x_device.panu_connection), FALSE);
+				}
+			}
+		}
+	}
+
+	bzobj->x_device_is_connected =    device_is_usable
+	                               && _bzobjs_device_is_connected (bzobj);
+
+	bzobj->x_device_is_usable = device_is_usable;
+
+	if (bzobj->x_device.device_bt) {
+		const char *device_to_delete_msg;
+
+		if (!device_is_usable)
+			device_to_delete_msg = "device became unusable";
+		else if (!_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
+		                                         bzobj->object_path,
+		                                         bzobj->d_device.address,
+		                                         NULL,
+		                                         bzobj->d_device_capabilities))
+			device_to_delete_msg = "device is no longer compatible";
+		else
+			device_to_delete_msg = NULL;
+
+		if (device_to_delete_msg) {
+			nm_clear_g_signal_handler (bzobj->x_device.device_bt, &bzobj->x_device.device_bt_signal_id);
+
+			device_deleted = g_steal_pointer (&bzobj->x_device.device_bt);
+
+			_LOGD ("[%s]: drop device because %s",
+			       bzobj->object_path,
+			       device_to_delete_msg);
+
+			_connect_disconnect (self, bzobj, device_to_delete_msg);
+		}
+	}
+
+	if (device_is_usable) {
+		if (!bzobj->x_device.device_bt) {
+			bzobj->x_device.device_bt = nm_device_bt_new (self,
+			                                              bzobj->object_path,
+			                                              bzobj->d_device.address,
+			                                              bzobj->d_device.name,
+			                                              bzobj->d_device_capabilities);
+			device_added = g_object_ref (bzobj->x_device.device_bt);
+			bzobj->x_device.device_bt_signal_id = g_signal_connect (device_added,
+			                                                        NM_DEVICE_STATE_CHANGED,
+			                                                        G_CALLBACK (_device_state_changed_cb),
+			                                                        bzobj);
+		} else
+			_nm_device_bt_notify_set_name (bzobj->x_device.device_bt, bzobj->d_device.name);
+
+		_nm_device_bt_notify_set_connected (bzobj->x_device.device_bt, bzobj->x_device_is_connected);
+	}
+
+	if (   bzobj->x_device.c_req_data
+	    && !bzobj->x_device.c_req_data->int_cancellable
+	    && bzobj->x_device_is_connected) {
+		gs_free char *device_name = g_steal_pointer (&bzobj->x_device.c_req_data->device_name);
+
+		_device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
+		                                   self,
+		                                   device_name,
+		                                   NULL);
+	}
+
+	if (device_added)
+		g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device_added);
+
+	if (device_deleted)
+		_nm_device_bt_notify_removed (device_deleted);
+}
+
+/*****************************************************************************/
+
+static void
+_process_change_idle_all (NMBluezManager *self,
+                          gboolean *out_emit_device_availability_changed)
 {
-	NMBluez5Manager *manager;
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	BzDBusObj *bzobj;
+
+	while ((bzobj = c_list_first_entry (&priv->process_change_lst_head, BzDBusObj, process_change_lst))) {
+
+		c_list_unlink (&bzobj->process_change_lst);
+
+		_LOG_bzobj (bzobj, "before-processing");
+
+		_device_process_change (bzobj);
+
+		_network_server_process_change (bzobj, out_emit_device_availability_changed);
+
+		_LOG_bzobj (bzobj, "after-processing");
+
+		_bzobjs_del_if_dead (bzobj);
+	}
+
+	nm_clear_g_source (&priv->process_change_idle_id);
+}
 
-	g_return_if_fail (!priv->manager4 && !priv->manager5 && !priv->bluez_version);
+static gboolean
+_process_change_idle_cb (gpointer user_data)
+{
+	NMBluezManager *self = user_data;
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	gboolean emit_device_availability_changed = FALSE;
 
-	setup_version_number (self, 5);
-	priv->manager5 = manager = nm_bluez5_manager_new (priv->settings);
+	_process_change_idle_all (self, &emit_device_availability_changed);
 
-	g_signal_connect (manager,
-	                  NM_BLUEZ_MANAGER_BDADDR_ADDED,
-	                  G_CALLBACK (manager_bdaddr_added_cb),
-	                  self);
-	g_signal_connect (manager,
-	                  NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED,
-	                  G_CALLBACK (manager_network_server_added_cb),
-	                  self);
+	if (emit_device_availability_changed)
+		nm_manager_notify_device_availibility_maybe_changed (priv->manager);
 
-	nm_bluez5_manager_query_devices (manager);
+	return G_SOURCE_CONTINUE;
 }
 
 static void
-watch_name_on_appeared (GDBusConnection *connection,
-                        const char *name,
-                        const char *name_owner,
-                        gpointer user_data)
+_process_change_idle_schedule (NMBluezManager *self,
+                               BzDBusObj *bzobj)
 {
-	check_bluez_and_try_setup (NM_BLUEZ_MANAGER (user_data));
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
+	if (priv->process_change_idle_id == 0)
+		priv->process_change_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, _process_change_idle_cb, self, NULL);
 }
 
 static void
-check_bluez_and_try_setup_final_step (NMBluezManager *self, int bluez_version, const char *reason)
+_dbus_process_changes (NMBluezManager *self,
+                       BzDBusObj *bzobj,
+                       const char *log_reason)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	gboolean network_server_is_usable;
+	gboolean adapter_is_usable_for_device;
+	gboolean device_is_usable;
+	gboolean changes = FALSE;
+	gboolean recheck_devices_for_adapter = FALSE;
+
+	nm_assert (bzobj);
+
+	_LOG_bzobj (bzobj, log_reason);
+
+	device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+
+	if (bzobj->x_device_is_usable != device_is_usable)
+		changes = TRUE;
+	else if (bzobj->x_device.device_bt) {
+		if (!device_is_usable)
+			changes = TRUE;
+		else {
+			if (   bzobj->x_device_is_connected != _bzobjs_device_is_connected (bzobj)
+			    || !_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
+			                                       bzobj->object_path,
+			                                       bzobj->d_device.address,
+			                                       bzobj->d_device.name,
+			                                       bzobj->d_device_capabilities))
+				changes = TRUE;
+		}
+	}
+
+	adapter_is_usable_for_device = _bzobjs_adapter_is_usable_for_device (bzobj);
+	if (adapter_is_usable_for_device != bzobj->was_usable_adapter_for_device_before) {
+		/* this function does not modify bzobj in any other cases except here.
+		 * Usually changes are processed delayed, in the idle handler.
+		 *
+		 * But the bzobj->was_usable_adapter_for_device_before only exists to know whether
+		 * we need to re-check device availability. It is correct to set the flag
+		 * here, right before we checked. */
+		bzobj->was_usable_adapter_for_device_before = adapter_is_usable_for_device;
+		recheck_devices_for_adapter = TRUE;
+		changes = TRUE;
+	}
+
+	if (!changes) {
+		network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+		if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
+			changes = TRUE;
+		else if (   bzobj->x_network_server.device_br
+		         && !network_server_is_usable)
+			changes = TRUE;
+		else if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL,
+		                     bzobj->x_network_server.adapter_address))
+			changes = TRUE;
+	}
+
+	if (changes)
+		_process_change_idle_schedule (self, bzobj);
+
+	if (recheck_devices_for_adapter) {
+		GHashTableIter iter;
+		BzDBusObj *bzobj2;
+
+		/* we got a change to the availability of an adapter. We might need to recheck
+		 * all devices that use this adapter... */
+		g_hash_table_iter_init (&iter, priv->bzobjs);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj2, NULL)) {
+			if (bzobj2 == bzobj)
+				continue;
+			if (!nm_streq0 (bzobj2->d_device.adapter, bzobj->object_path))
+				continue;
+			if (c_list_is_empty (&bzobj2->process_change_lst))
+				_dbus_process_changes (self, bzobj2, "adapter-changed");
+			else
+				nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj2->process_change_lst);
+		}
+	}
+
+	_bzobjs_del_if_dead (bzobj);
+}
+
+/*****************************************************************************/
+
+#define ALL_RELEVANT_INTERFACE_NAMES NM_MAKE_STRV (NM_BLUEZ5_ADAPTER_INTERFACE, \
+                                                   NM_BLUEZ5_DEVICE_INTERFACE, \
+                                                   NM_BLUEZ5_NETWORK_INTERFACE, \
+                                                   NM_BLUEZ5_NETWORK_SERVER_INTERFACE)
+
+static gboolean
+_dbus_handle_properties_changed (NMBluezManager *self,
+                                 const char *object_path,
+                                 const char *interface_name,
+                                 GVariant *changed_properties,
+                                 const char *const*invalidated_properties,
+                                 BzDBusObj **inout_bzobj)
+{
+	BzDBusObj *bzobj = NULL;
+	gboolean changed = FALSE;
+	const char *property_name;
+	GVariant *property_value;
+	GVariantIter iter_prop;
+	gsize i;
 
-	g_return_if_fail (!priv->bluez_version);
+	if (!invalidated_properties)
+		invalidated_properties = NM_PTRARRAY_EMPTY (const char *);
 
-	switch (bluez_version) {
-	case 4:
-		setup_bluez4 (self);
-		break;
-	case 5:
-		setup_bluez5 (self);
-		break;
-	default:
-		_LOGD ("detecting BlueZ version failed: %s", reason);
+	nm_assert (g_variant_is_of_type (changed_properties, G_VARIANT_TYPE ("a{sv}")));
 
-		/* cancel current attempts to detect the version. */
-		cleanup_checking (self, FALSE);
-		if (!priv->watch_name_id) {
-			priv->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
-			                                        NM_BLUEZ_SERVICE,
-			                                        G_BUS_NAME_WATCHER_FLAGS_NONE,
-			                                        watch_name_on_appeared,
-			                                        NULL,
-			                                        self,
-			                                        NULL);
+	if (inout_bzobj) {
+		bzobj = *inout_bzobj;
+		nm_assert (!bzobj || nm_streq (object_path, bzobj->object_path));
+	}
+
+	if (changed_properties)
+		g_variant_iter_init (&iter_prop, changed_properties);
+
+	if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
+		_bzobjs_init (self, &bzobj, object_path);
+		if (!bzobj->d_has_adapter_iface) {
+			changed = TRUE;
+			bzobj->d_has_adapter_iface = TRUE;
+		}
+
+		while (   changed_properties
+		       && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+			_nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+			if (nm_streq (property_name, "Address")) {
+				gs_free char *s =   g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+				                  ? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
+				                  : NULL;
+
+				if (!nm_streq0 (bzobj->d_adapter.address, s)) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_adapter.address);
+					bzobj->d_adapter.address = g_steal_pointer (&s);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Powered")) {
+				bool v =    g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+				         && g_variant_get_boolean (property_value);
+
+				if (bzobj->d_adapter_powered != v) {
+					changed = TRUE;
+					bzobj->d_adapter_powered = v;
+				}
+				continue;
+			}
+		}
+
+		for (i = 0; (property_name = invalidated_properties[i]); i++) {
+			if (nm_streq (property_name, "Address")) {
+				if (bzobj->d_adapter.address) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_adapter.address);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Powered")) {
+				if (bzobj->d_adapter_powered) {
+					changed = TRUE;
+					bzobj->d_adapter_powered = FALSE;
+				}
+				continue;
+			}
+		}
+
+	} else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
+		_bzobjs_init (self, &bzobj, object_path);
+		if (!bzobj->d_has_device_iface) {
+			changed = TRUE;
+			bzobj->d_has_device_iface = TRUE;
+		}
+
+		while (   changed_properties
+		       && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+			_nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+			if (nm_streq (property_name, "Address")) {
+				gs_free char *s =   g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+				                  ? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
+				                  : NULL;
+
+				if (!nm_streq0 (bzobj->d_device.address, s)) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_device.address);
+					bzobj->d_device.address = g_steal_pointer (&s);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Name")) {
+				const char *s =   g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+				                ? g_variant_get_string (property_value, NULL)
+				                : NULL;
+
+				if (!nm_streq0 (bzobj->d_device.name, s)) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_device.name);
+					bzobj->d_device.name = g_strdup (s);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Adapter")) {
+				const char *s =   g_variant_is_of_type (property_value, G_VARIANT_TYPE_OBJECT_PATH)
+				                ? g_variant_get_string (property_value, NULL)
+				                : NULL;
+
+				if (!nm_streq0 (bzobj->d_device.adapter, s)) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_device.adapter);
+					bzobj->d_device.adapter = g_strdup (s);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "UUIDs")) {
+				NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
+
+				if (g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING_ARRAY)) {
+					gs_free const char **s = g_variant_get_strv (property_value, NULL);
+
+					capabilities = convert_uuids_to_capabilities (s);
+				}
+				if (bzobj->d_device_capabilities != capabilities) {
+					changed = TRUE;
+					bzobj->d_device_capabilities = capabilities;
+					nm_assert (bzobj->d_device_capabilities == capabilities);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Connected")) {
+				bool v =    g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+				         && g_variant_get_boolean (property_value);
+
+				if (bzobj->d_device_connected != v) {
+					changed = TRUE;
+					bzobj->d_device_connected = v;
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Paired")) {
+				bool v =    g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+				         && g_variant_get_boolean (property_value);
+
+				if (bzobj->d_device_paired != v) {
+					changed = TRUE;
+					bzobj->d_device_paired = v;
+				}
+				continue;
+			}
+		}
+
+		for (i = 0; (property_name = invalidated_properties[i]); i++) {
+			if (nm_streq (property_name, "Address")) {
+				if (bzobj->d_device.address) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_device.address);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Name")) {
+				if (bzobj->d_device.name) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_device.name);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Adapter")) {
+				if (bzobj->d_device.adapter) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_device.adapter);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "UUIDs")) {
+				if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+					changed = TRUE;
+					bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Connected")) {
+				if (bzobj->d_device_connected) {
+					changed = TRUE;
+					bzobj->d_device_connected = FALSE;
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Paired")) {
+				if (bzobj->d_device_paired) {
+					changed = TRUE;
+					bzobj->d_device_paired = FALSE;
+				}
+				continue;
+			}
+		}
+
+	} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
+		_bzobjs_init (self, &bzobj, object_path);
+		if (!bzobj->d_has_network_iface) {
+			changed = TRUE;
+			bzobj->d_has_network_iface = TRUE;
+		}
+
+		while (   changed_properties
+		       && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+			_nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+			if (nm_streq (property_name, "Interface")) {
+				const char *s =   g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+				                ? g_variant_get_string (property_value, NULL)
+				                : NULL;
+
+				if (!nm_streq0 (bzobj->d_network.interface, s)) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_network.interface);
+					bzobj->d_network.interface = g_strdup (s);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Connected")) {
+				bool v =    g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+				         && g_variant_get_boolean (property_value);
+
+				if (bzobj->d_network_connected != v) {
+					changed = TRUE;
+					bzobj->d_network_connected = v;
+				}
+				continue;
+			}
+		}
+
+		for (i = 0; (property_name = invalidated_properties[i]); i++) {
+			if (nm_streq (property_name, "Interface")) {
+				if (bzobj->d_network.interface) {
+					changed = TRUE;
+					nm_clear_g_free (&bzobj->d_network.interface);
+				}
+				continue;
+			}
+			if (nm_streq (property_name, "Connected")) {
+				if (bzobj->d_network_connected) {
+					changed = TRUE;
+					bzobj->d_network_connected = FALSE;
+				}
+				continue;
+			}
+		}
+
+	} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
+		_bzobjs_init (self, &bzobj, object_path);
+		if (!bzobj->d_has_network_server_iface) {
+			changed = TRUE;
+			bzobj->d_has_network_server_iface = TRUE;
 		}
-		break;
 	}
+
+	nm_assert (!changed || bzobj);
+
+	if (inout_bzobj)
+		*inout_bzobj = bzobj;
+
+	return changed;
 }
 
 static void
-check_bluez_and_try_setup_do_introspect (GObject *source_object,
-                                         GAsyncResult *res,
-                                         gpointer user_data)
+_dbus_handle_interface_added (NMBluezManager *self,
+                              const char *object_path,
+                              GVariant *ifaces,
+                              gboolean initial_get_managed_objects)
+{
+	BzDBusObj *bzobj = NULL;
+	gboolean changed = FALSE;
+	const char *interface_name;
+	GVariant *changed_properties;
+	GVariantIter iter_ifaces;
+
+	nm_assert (g_variant_is_of_type (ifaces, G_VARIANT_TYPE ("a{sa{sv}}")));
+
+	g_variant_iter_init (&iter_ifaces, ifaces);
+	while (g_variant_iter_next (&iter_ifaces, "{&s@a{sv}}", &interface_name, &changed_properties)) {
+		_nm_unused gs_unref_variant GVariant *changed_properties_free = changed_properties;
+
+		if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, NULL, &bzobj))
+			changed = TRUE;
+	}
+
+	if (changed) {
+		_dbus_process_changes (self,
+		                       bzobj,
+		                         initial_get_managed_objects
+		                       ? "dbus-init"
+		                       : "dbus-iface-added");
+	}
+}
+
+static gboolean
+_dbus_handle_interface_removed (NMBluezManager *self,
+                                const char *object_path,
+                                BzDBusObj **inout_bzobj,
+                                const char *const*removed_interfaces)
 {
-	NMBluezManager *self = async_data_unpack (user_data);
+	gboolean changed = FALSE;
+	BzDBusObj *bzobj;
+	gsize i;
+
+	if (   inout_bzobj
+	    && *inout_bzobj) {
+		bzobj = *inout_bzobj;
+		nm_assert (bzobj == _bzobjs_get (self, object_path));
+	} else {
+		bzobj = _bzobjs_get (self, object_path);
+		if (!bzobj)
+			return FALSE;
+		NM_SET_OUT (inout_bzobj, bzobj);
+	}
+
+	for (i = 0; removed_interfaces[i]; i++) {
+		const char *interface_name = removed_interfaces[i];
+
+		if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
+			if (bzobj->d_has_adapter_iface) {
+				changed = TRUE;
+				bzobj->d_has_adapter_iface = FALSE;
+			}
+			if (bzobj->d_adapter.address) {
+				changed = TRUE;
+				nm_clear_g_free (&bzobj->d_adapter.address);
+			}
+			if (bzobj->d_adapter_powered) {
+				changed = TRUE;
+				bzobj->d_adapter_powered = FALSE;
+			}
+		} else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
+			if (bzobj->d_has_device_iface) {
+				changed = TRUE;
+				bzobj->d_has_device_iface = FALSE;
+			}
+			if (bzobj->d_device.address) {
+				changed = TRUE;
+				nm_clear_g_free (&bzobj->d_device.address);
+			}
+			if (bzobj->d_device.name) {
+				changed = TRUE;
+				nm_clear_g_free (&bzobj->d_device.name);
+			}
+			if (bzobj->d_device.adapter) {
+				changed = TRUE;
+				nm_clear_g_free (&bzobj->d_device.adapter);
+			}
+			if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+				changed = TRUE;
+				bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
+			}
+			if (bzobj->d_device_connected) {
+				changed = TRUE;
+				bzobj->d_device_connected = FALSE;
+			}
+			if (bzobj->d_device_paired) {
+				changed = TRUE;
+				bzobj->d_device_paired = FALSE;
+			}
+		} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
+			if (bzobj->d_has_network_iface) {
+				changed = TRUE;
+				bzobj->d_has_network_iface = FALSE;
+			}
+			if (bzobj->d_network.interface) {
+				changed = TRUE;
+				nm_clear_g_free (&bzobj->d_network.interface);
+			}
+			if (bzobj->d_network_connected) {
+				changed = TRUE;
+				bzobj->d_network_connected = FALSE;
+			}
+		} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
+			if (bzobj->d_has_network_server_iface) {
+				changed = TRUE;
+				bzobj->d_has_network_server_iface = FALSE;
+			}
+		}
+	}
+
+	return changed;
+}
+
+static void
+_dbus_managed_objects_changed_cb (const char *object_path,
+                                  GVariant *added_interfaces_and_properties,
+                                  const char *const*removed_interfaces,
+                                  gpointer user_data)
+{
+	NMBluezManager *self = user_data;
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	BzDBusObj *bzobj = NULL;
+	gboolean changed;
+
+	if (priv->get_managed_objects_cancellable) {
+		/* we still wait for the initial GetManagedObjects(). Ignore the event. */
+		return;
+	}
+
+	if (!added_interfaces_and_properties) {
+		changed = _dbus_handle_interface_removed (self, object_path, &bzobj, removed_interfaces);
+		if (changed)
+			_dbus_process_changes (self, bzobj, "dbus-iface-removed");
+	} else
+		_dbus_handle_interface_added (self, object_path, added_interfaces_and_properties, FALSE);
+}
+
+static void
+_dbus_properties_changed_cb (GDBusConnection *connection,
+                             const char *sender_name,
+                             const char *object_path,
+                             const char *signal_interface_name,
+                             const char *signal_name,
+                             GVariant *parameters,
+                             gpointer user_data)
+{
+	NMBluezManager *self = user_data;
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	const char *interface_name;
+	gs_unref_variant GVariant *changed_properties = NULL;
+	gs_free const char **invalidated_properties = NULL;
+	BzDBusObj *bzobj = NULL;
+
+	if (priv->get_managed_objects_cancellable) {
+		/* we still wait for the initial GetManagedObjects(). Ignore the event. */
+		return;
+	}
+
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
+		return;
+
+	g_variant_get (parameters,
+	               "(&s@a{sv}^a&s)",
+	               &interface_name,
+	               &changed_properties,
+	               &invalidated_properties);
+
+	if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, invalidated_properties, &bzobj))
+		_dbus_process_changes (self, bzobj, "dbus-property-changed");
+}
+
+static void
+_dbus_get_managed_objects_cb (GVariant *result,
+                              GError *error,
+                              gpointer user_data)
+{
+	NMBluezManager *self;
 	NMBluezManagerPrivate *priv;
-	GError *error = NULL;
-	GVariant *result;
-	const char *xml_data;
-	int bluez_version = 0;
-	const char *reason = NULL;
+	GVariantIter iter;
+	const char *object_path;
+	GVariant *ifaces;
 
-	if (!self)
+	if (   !result
+	    && nm_utils_error_is_cancelled (error, FALSE))
 		return;
 
+	self = user_data;
 	priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
 
-	g_return_if_fail (priv->introspect_proxy);
-	g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
-	g_return_if_fail (!priv->bluez_version);
-
-	g_clear_object (&priv->async_cancellable);
+	g_clear_object (&priv->get_managed_objects_cancellable);
 
-	result = _nm_dbus_proxy_call_finish (priv->introspect_proxy, res,
-	                                     G_VARIANT_TYPE ("(s)"), &error);
 	if (!result) {
-		char *reason2;
-
-		g_dbus_error_strip_remote_error (error);
-		reason2 = g_strdup_printf ("introspect failed with %s", error->message);
-		check_bluez_and_try_setup_final_step (self, 0, reason2);
-		g_error_free (error);
-		g_free (reason2);
+		_LOGT ("initial GetManagedObjects() call failed: %s", error->message);
+		_cleanup_for_name_owner (self);
 		return;
 	}
 
-	g_variant_get (result, "(&s)", &xml_data);
+	_LOGT ("initial GetManagedObjects call succeeded");
+
+	g_variant_iter_init (&iter, result);
+	while (g_variant_iter_next (&iter, "{&o@a{sa{sv}}}", &object_path, &ifaces)) {
+		_nm_unused gs_unref_variant GVariant *ifaces_free = ifaces;
+
+		_dbus_handle_interface_added (self, object_path, ifaces, TRUE);
+	}
+}
+
+/*****************************************************************************/
+
+static void
+_cleanup_for_name_owner (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	gboolean emit_device_availability_changed = FALSE;
+	GHashTableIter iter;
+	BzDBusObj *bzobj;
+	gboolean first = TRUE;
+
+	nm_clear_g_cancellable (&priv->get_managed_objects_cancellable);
+
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->managed_objects_changed_id);
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->properties_changed_id);
+
+	nm_clear_g_free (&priv->name_owner);
+
+	g_hash_table_iter_init (&iter, priv->bzobjs);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+		if (first) {
+			first = FALSE;
+			_LOGT ("drop all objects form D-Bus cache...");
+		}
+		_dbus_handle_interface_removed (self,
+		                                bzobj->object_path,
+		                                &bzobj,
+		                                ALL_RELEVANT_INTERFACE_NAMES);
+		nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
+	}
+	_process_change_idle_all (self, &emit_device_availability_changed);
+	nm_assert (g_hash_table_size (priv->bzobjs) == 0);
+
+	if (emit_device_availability_changed)
+		nm_manager_notify_device_availibility_maybe_changed (priv->manager);
+}
 
-	/* might not be the best approach to detect the version, but it's good enough in practice. */
-	if (strstr (xml_data, "org.freedesktop.DBus.ObjectManager"))
-		bluez_version = 5;
-	else if (strstr (xml_data, NM_BLUEZ4_MANAGER_INTERFACE))
-		bluez_version = 4;
+static void
+name_owner_changed (NMBluezManager *self,
+                    const char *owner)
+{
+	_nm_unused gs_unref_object NMBluezManager *self_keep_alive = g_object_ref (self);
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	owner = nm_str_not_empty (owner);
+
+	if (!owner)
+		_LOGT ("D-Bus name for bluez has no owner");
 	else
-		reason = "unexpected introspect result";
+		_LOGT ("D-Bus name for bluez has owner %s", owner);
+
+	nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
+
+	if (nm_streq0 (priv->name_owner, owner))
+		return;
+
+	_cleanup_for_name_owner (self);
 
-	g_variant_unref (result);
+	if (!owner)
+		return;
 
-	check_bluez_and_try_setup_final_step (self, bluez_version, reason);
+	priv->name_owner = g_strdup (owner);
+
+	priv->get_managed_objects_cancellable = g_cancellable_new ();
+
+	priv->managed_objects_changed_id = nm_dbus_connection_signal_subscribe_object_manager (priv->dbus_connection,
+	                                                                                       priv->name_owner,
+	                                                                                       NM_BLUEZ_MANAGER_PATH,
+	                                                                                       _dbus_managed_objects_changed_cb,
+	                                                                                       self,
+	                                                                                       NULL);
+
+	priv->properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection,
+	                                                                                      priv->name_owner,
+	                                                                                      NULL,
+	                                                                                      NULL,
+	                                                                                      _dbus_properties_changed_cb,
+	                                                                                      self,
+	                                                                                      NULL);
+
+	nm_dbus_connection_call_get_managed_objects (priv->dbus_connection,
+	                                             priv->name_owner,
+	                                             NM_BLUEZ_MANAGER_PATH,
+	                                             G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                                             20000,
+	                                             priv->get_managed_objects_cancellable,
+	                                             _dbus_get_managed_objects_cb,
+	                                             self);
 }
 
 static void
-check_bluez_and_try_setup_on_new_proxy (GObject *source_object,
-                                        GAsyncResult *res,
-                                        gpointer user_data)
+name_owner_changed_cb (GDBusConnection *connection,
+                       const char *sender_name,
+                       const char *object_path,
+                       const char *interface_name,
+                       const char *signal_name,
+                       GVariant *parameters,
+                       gpointer user_data)
 {
-	NMBluezManager *self = async_data_unpack (user_data);
-	NMBluezManagerPrivate *priv;
-	GError *error = NULL;
+	NMBluezManager *self = user_data;
+	const char *new_owner;
 
-	if (!self)
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
 		return;
 
+	g_variant_get (parameters,
+	               "(&s&s&s)",
+	               NULL,
+	               NULL,
+	               &new_owner);
+
+	name_owner_changed (self, new_owner);
+}
+
+static void
+name_owner_get_cb (const char *name_owner,
+                   GError *error,
+                   gpointer user_data)
+{
+	if (   name_owner
+	    || !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		name_owner_changed (user_data, name_owner);
+}
+
+/*****************************************************************************/
+
+static void
+_cleanup_all (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	priv->settings_registered = FALSE;
+
+	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);
+
+	g_hash_table_remove_all (priv->conn_data_elems);
+	g_hash_table_remove_all (priv->conn_data_heads);
+
+	_cleanup_for_name_owner (self);
+
+	nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
+
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->name_owner_changed_id);
+}
+
+static void
+start (NMDeviceFactory *factory)
+{
+	NMBluezManager *self;
+	NMBluezManagerPrivate *priv;
+	NMSettingsConnection *const*sett_conns;
+	guint n_sett_conns;
+	guint i;
+
+	g_return_if_fail (NM_IS_BLUEZ_MANAGER (factory));
+
+	self = NM_BLUEZ_MANAGER (factory);
 	priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
 
-	g_return_if_fail (!priv->introspect_proxy);
-	g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
-	g_return_if_fail (!priv->bluez_version);
+	_cleanup_all (self);
+
+	if (!priv->dbus_connection) {
+		_LOGI ("no D-Bus connection available");
+		return;
+	}
+
+	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_UPDATED, G_CALLBACK (cp_connection_updated), self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
 
-	priv->introspect_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+	priv->settings_registered = TRUE;
 
-	if (!priv->introspect_proxy) {
-		char *reason = g_strdup_printf ("bluez error creating dbus proxy: %s", error->message);
-		check_bluez_and_try_setup_final_step (self, 0, reason);
-		g_error_free (error);
-		g_free (reason);
+	sett_conns = nm_settings_get_connections (priv->settings, &n_sett_conns);
+	for (i = 0; i < n_sett_conns; i++)
+		_conn_track_update (self, sett_conns[i], TRUE, NULL, NULL, NULL);
+
+	priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+	                                                                                      NM_BLUEZ_SERVICE,
+	                                                                                      name_owner_changed_cb,
+	                                                                                      self,
+	                                                                                      NULL);
+
+	priv->name_owner_get_cancellable = g_cancellable_new ();
+
+	nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+	                                        NM_BLUEZ_SERVICE,
+	                                        10000,
+	                                        priv->name_owner_get_cancellable,
+	                                        name_owner_get_cb,
+	                                        self);
+}
+
+/*****************************************************************************/
+
+static void
+_connect_returned (NMBluezManager *self,
+                   BzDBusObj *bzobj,
+                   NMBluetoothCapabilities bt_type,
+                   const char *device_name,
+                   NMBluez5DunContext *dun_context,
+                   GError *error)
+{
+	char sbuf_cap[100];
+
+	if (error) {
+		nm_assert (!device_name);
+		nm_assert (!dun_context);
+
+		_LOGI ("%s [%s]: connect failed: %s",
+		       nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+		       bzobj->object_path,
+		       error->message);
+
+		_device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
+		                                   self,
+		                                   NULL,
+		                                   error);
+		_connect_disconnect (self, bzobj, "cleanup after connect failure");
 		return;
 	}
 
-	g_dbus_proxy_call (priv->introspect_proxy,
-	                   "Introspect",
-	                   NULL,
-	                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
-	                   3000,
-	                   priv->async_cancellable,
-	                   check_bluez_and_try_setup_do_introspect,
-	                   async_data_pack (self));
+	nm_assert (bzobj->x_device_connect_bt_type == bt_type);
+	nm_assert (device_name);
+	nm_assert ((bt_type == NM_BT_CAPABILITY_DUN) == (!!dun_context));
+	nm_assert (bzobj->x_device.c_req_data);
+
+	g_clear_object (&bzobj->x_device.c_req_data->int_cancellable);
+
+	bzobj->x_device.connect_dun_context = dun_context;
+
+	_LOGD ("%s [%s]: connect successful to device %s",
+	       nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+	       bzobj->object_path,
+	       device_name);
+
+	/* we already have another over-all timer running. But after we connected the device,
+	 * we still need to wait for bluez to acknowledge the connected state (via D-Bus, for NAP).
+	 * For DUN profiles we likely are already fully connected by now.
+	 *
+	 * Anyway, schedule another timeout that is possibly shorter than the overall, original
+	 * timeout. Now this should go down fast. */
+	bzobj->x_device.c_req_data->timeout_wait_connect_id = g_timeout_add (5000,
+	                                                                     _connect_timeout_wait_connected_cb,
+	                                                                     bzobj),
+	bzobj->x_device.c_req_data->device_name = g_strdup (device_name);
+
+	if (   _bzobjs_device_is_usable (bzobj, NULL, NULL)
+	    && _bzobjs_device_is_connected (bzobj)) {
+		/* We are now connected. Schedule the task that completes the state. */
+		_process_change_idle_schedule (self, bzobj);
+	}
+}
+
+#if WITH_BLUEZ5_DUN
+static void
+_connect_dun_notify_tty_hangup_cb (NMBluez5DunContext *context,
+                                   gpointer user_data)
+{
+	BzDBusObj *bzobj = user_data;
+
+	_connect_disconnect (bzobj->self,
+	                     bzobj,
+	                     "DUN connection hung up");
 }
 
 static void
-check_bluez_and_try_setup (NMBluezManager *self)
+_connect_dun_step2_cb (NMBluez5DunContext *context,
+                       const char *rfcomm_dev,
+                       GError *error,
+                       gpointer user_data)
 {
-	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	BzDBusObj *bzobj;
+
+	if (nm_utils_error_is_cancelled (error, FALSE))
+		return;
+
+	bzobj = user_data;
+
+	if (rfcomm_dev) {
+		/* We want to early notifiy about the rfcomm path. That is because we might still delay
+		 * to signal full activation longer (asynchronously). But the earliest time the callback
+		 * is invoked with the rfcomm path, we just created the device synchronously.
+		 *
+		 * By already notifying the caller about the path early, it avoids a race where ModemManager
+		 * would find the modem before the bluetooth code considers the profile fully activated. */
 
-	g_return_if_fail (!priv->bluez_version);
+		nm_assert (!error);
+		nm_assert (bzobj->x_device.c_req_data);
 
-	/* there should be no ongoing detection. Anyway, cleanup_checking. */
-	cleanup_checking (self, FALSE);
+		if (!g_cancellable_is_cancelled (bzobj->x_device.c_req_data->ext_cancellable))
+			bzobj->x_device.c_req_data->callback (bzobj->self, FALSE, rfcomm_dev, NULL, bzobj->x_device.c_req_data->callback_user_data);
 
-	priv->async_cancellable = g_cancellable_new ();
+		if (!context) {
+			/* No context set. This means, we just got notified about the rfcomm path and need to wait
+			 * longer, for the next callback. */
+			return;
+		}
+	}
 
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
-	                          NULL,
-	                          NM_BLUEZ_SERVICE,
-	                          "/",
-	                          DBUS_INTERFACE_INTROSPECTABLE,
-	                          priv->async_cancellable,
-	                          check_bluez_and_try_setup_on_new_proxy,
-	                          async_data_pack (self));
+	_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, rfcomm_dev, context, error);
 }
 
 static void
-start (NMDeviceFactory *factory)
+_connect_dun_step1_cb (GObject *source_object,
+                       GAsyncResult *res,
+                       gpointer user_data)
+{
+	gs_unref_variant GVariant *ret = NULL;
+	gs_free_error GError *error = NULL;
+	BzDBusObj *bzobj_adapter;
+	BzDBusObj *bzobj;
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+
+	if (   !ret
+	    && nm_utils_error_is_cancelled (error, FALSE))
+		return;
+
+	bzobj = user_data;
+
+	if (error) {
+		_LOGT ("DUN: [%s]: bluetooth device connect failed: %s", bzobj->object_path, error->message);
+		/* we actually ignore this error. Let's try, maybe we still can connect via DUN. */
+		g_clear_error (&error);
+	} else
+		_LOGT ("DUN: [%s]: bluetooth device connected successfully", bzobj->object_path);
+
+	if (!_bzobjs_device_is_usable (bzobj, &bzobj_adapter, NULL)) {
+		nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
+		                    "device %s is not usable for DUN after connect",
+		                    bzobj->object_path);
+		_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
+		return;
+	}
+
+	if (!nm_bluez5_dun_connect (bzobj_adapter->d_adapter.address,
+	                            bzobj->d_device.address,
+	                            bzobj->x_device.c_req_data->int_cancellable,
+	                            _connect_dun_step2_cb,
+	                            bzobj,
+	                            _connect_dun_notify_tty_hangup_cb,
+	                            bzobj,
+	                            &error)) {
+		_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
+		return;
+	}
+}
+#endif
+
+static void
+_connect_nap_cb (GObject *source_object,
+                 GAsyncResult *res,
+                 gpointer user_data)
 {
-	check_bluez_and_try_setup (NM_BLUEZ_MANAGER (factory));
+	gs_unref_variant GVariant *ret = NULL;
+	const char *network_iface_name = NULL;
+	gs_free_error GError *error = NULL;
+	BzDBusObj *bzobj;
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+
+	if (   !ret
+	    && nm_utils_error_is_cancelled (error, FALSE))
+		return;
+
+	if (ret)
+		g_variant_get (ret, "(&s)", &network_iface_name);
+
+	bzobj = user_data;
+
+	_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_NAP, network_iface_name, NULL, error);
+}
+
+static void
+_connect_cancelled_cb (GCancellable *cancellable,
+                       BzDBusObj *bzobj)
+{
+	_connect_disconnect (bzobj->self, bzobj, "connect cancelled");
 }
 
+static gboolean
+_connect_timeout_wait_connected_cb (gpointer user_data)
+{
+	BzDBusObj *bzobj = user_data;
+
+	bzobj->x_device.c_req_data->timeout_wait_connect_id = 0;
+	_connect_disconnect (bzobj->self, bzobj, "timeout waiting for connected");
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_timeout_cb (gpointer user_data)
+{
+	BzDBusObj *bzobj = user_data;
+
+	bzobj->x_device.c_req_data->timeout_id = 0;
+	_connect_disconnect (bzobj->self, bzobj, "timeout connecting");
+	return G_SOURCE_REMOVE;
+}
+
+static void
+_connect_disconnect (NMBluezManager *self,
+                     BzDBusObj *bzobj,
+                     const char *reason)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	DeviceConnectReqData *c_req_data;
+	char sbuf_cap[100];
+	gboolean bt_type;
+
+	if (bzobj->x_device_connect_bt_type == NM_BT_CAPABILITY_NONE) {
+		nm_assert (!bzobj->x_device.c_req_data);
+		return;
+	}
+
+	bt_type = bzobj->x_device_connect_bt_type;
+	nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN, NM_BT_CAPABILITY_NAP));
+	bzobj->x_device_connect_bt_type = NM_BT_CAPABILITY_NONE;
+
+	c_req_data = g_steal_pointer (&bzobj->x_device.c_req_data);
+
+	_LOGD ("%s [%s]: disconnect due to %s",
+	       nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
+	       bzobj->object_path,
+	       reason);
+
+	if (c_req_data)
+		nm_clear_g_cancellable (&c_req_data->int_cancellable);
+
+	if (bt_type == NM_BT_CAPABILITY_DUN) {
+		/* For DUN devices, we also called org.bluez.Device1.Connect() (because in order
+		 * for nm_bluez5_dun_connect() to succeed, we need to be already connected *why??).
+		 *
+		 * But upon disconnect we don't call Disconnect() because we don't know whether somebody
+		 * else also uses the bluetooth device for other purposes. During disconnect we only
+		 * terminate the DUN connection, but don't disconnect entirely. I think that's the
+		 * best we can do. */
+#if WITH_BLUEZ5_DUN
+		nm_clear_pointer (&bzobj->x_device.connect_dun_context, nm_bluez5_dun_disconnect);
+#else
+		nm_assert_not_reached ();
+#endif
+	} else {
+		if (priv->name_owner) {
+			gs_unref_object GCancellable *cancellable = NULL;
+
+			cancellable = g_cancellable_new ();
+
+			nm_shutdown_wait_obj_register_cancellable_full (cancellable,
+			                                                g_strdup_printf ("bt-disconnect-nap[%s]", bzobj->object_path),
+			                                                TRUE);
+
+			g_dbus_connection_call (priv->dbus_connection,
+			                        priv->name_owner,
+			                        bzobj->object_path,
+			                        NM_BLUEZ5_NETWORK_INTERFACE,
+			                        "Disconnect",
+			                        g_variant_new("()"),
+			                        NULL,
+			                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+			                        -1,
+			                        cancellable,
+			                        _dbus_call_complete_cb_nop,
+			                        NULL);
+		}
+	}
+
+	if (c_req_data) {
+		gs_free_error GError *error = NULL;
+
+		nm_utils_error_set (&error,
+		                    NM_UTILS_ERROR_UNKNOWN,
+		                    "connect aborted due to %s",
+		                    reason);
+		_device_connect_req_data_complete (c_req_data, self, NULL, error);
+	}
+}
+
+gboolean
+nm_bluez_manager_connect (NMBluezManager *self,
+                          const char *object_path,
+                          NMBluetoothCapabilities connection_bt_type,
+                          int timeout_msec,
+                          GCancellable *cancellable,
+                          NMBluezManagerConnectCb callback,
+                          gpointer callback_user_data,
+                          GError **error)
+{
+	gs_unref_object GCancellable *int_cancellable = NULL;
+	DeviceConnectReqData *c_req_data;
+	NMBluezManagerPrivate *priv;
+	BzDBusObj *bzobj;
+	char sbuf_cap[100];
+
+	g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), FALSE);
+	g_return_val_if_fail (NM_IN_SET (connection_bt_type, NM_BT_CAPABILITY_DUN,
+	                                                     NM_BT_CAPABILITY_NAP), FALSE);
+	g_return_val_if_fail (callback, FALSE);
+
+	nm_assert (timeout_msec > 0);
+
+	priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	bzobj = _bzobjs_get (self, object_path);
+
+	if (!bzobj) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    "device %s does not exist",
+		                    object_path);
+		return FALSE;
+	}
+
+	if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    "device %s is not usable",
+		                    object_path);
+		return FALSE;
+	}
+
+	if (!NM_FLAGS_ALL (bzobj->d_device_capabilities, connection_bt_type)) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    "device %s has not the required capabilities",
+		                    object_path);
+		return FALSE;
+	}
+
+#if !WITH_BLUEZ5_DUN
+	if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    "DUN is not supported");
+		return FALSE;
+	}
+#endif
+
+	_connect_disconnect (self, bzobj, "new activation");
+
+	_LOGD ("%s [%s]: connecting...",
+	       nm_bluetooth_capability_to_string (connection_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+	       bzobj->object_path);
+
+	int_cancellable = g_cancellable_new();
+
+#if WITH_BLUEZ5_DUN
+	if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+		g_dbus_connection_call (priv->dbus_connection,
+		                        priv->name_owner,
+		                        bzobj->object_path,
+		                        NM_BLUEZ5_DEVICE_INTERFACE,
+		                        "Connect",
+		                        NULL,
+		                        NULL,
+		                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                        timeout_msec,
+		                        int_cancellable,
+		                        _connect_dun_step1_cb,
+		                        bzobj);
+	} else
+#endif
+	{
+		nm_assert (connection_bt_type == NM_BT_CAPABILITY_NAP);
+		g_dbus_connection_call (priv->dbus_connection,
+		                        priv->name_owner,
+		                        bzobj->object_path,
+		                        NM_BLUEZ5_NETWORK_INTERFACE,
+		                        "Connect",
+		                        g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
+		                        G_VARIANT_TYPE ("(s)"),
+		                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                        timeout_msec,
+		                        int_cancellable,
+		                        _connect_nap_cb,
+		                        bzobj);
+	}
+
+	c_req_data = g_slice_new (DeviceConnectReqData);
+	*c_req_data = (DeviceConnectReqData) {
+		.int_cancellable     = g_steal_pointer (&int_cancellable),
+		.ext_cancellable     = g_object_ref (cancellable),
+		.callback            = callback,
+		.callback_user_data  = callback_user_data,
+		.ext_cancelled_id    = g_signal_connect (cancellable,
+		                                         "cancelled",
+		                                         G_CALLBACK (_connect_cancelled_cb),
+		                                         bzobj),
+		.timeout_id          = g_timeout_add (timeout_msec,
+		                                      _connect_timeout_cb,
+		                                      bzobj),
+	};
+
+	bzobj->x_device_connect_bt_type = connection_bt_type;
+	bzobj->x_device.c_req_data = c_req_data;
+
+	return TRUE;
+}
+
+void
+nm_bluez_manager_disconnect (NMBluezManager *self,
+                             const char *object_path)
+{
+	BzDBusObj *bzobj;
+
+	g_return_if_fail (NM_IS_BLUEZ_MANAGER (self));
+	g_return_if_fail (object_path);
+
+	bzobj = _bzobjs_get (self, object_path);
+	if (!bzobj)
+		return;
+
+	_connect_disconnect (self, bzobj, "disconnected by user");
+}
+
+/*****************************************************************************/
+
 static NMDevice *
 create_device (NMDeviceFactory *factory,
                const char *iface,
@@ -407,8 +2789,8 @@ create_device (NMDeviceFactory *factory,
                NMConnection *connection,
                gboolean *out_ignore)
 {
-	g_warn_if_fail (plink->type == NM_LINK_TYPE_BNEP);
 	*out_ignore = TRUE;
+	g_return_val_if_fail (plink->type == NM_LINK_TYPE_BNEP, NULL);
 	return NULL;
 }
 
@@ -433,7 +2815,25 @@ nm_bluez_manager_init (NMBluezManager *self)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
 
+	priv->vtable_network_server = (NMBtVTableNetworkServer) {
+		.is_available      = _network_server_vt_is_available,
+		.register_bridge   = _network_server_vt_register_bridge,
+		.unregister_bridge = _network_server_vt_unregister_bridge,
+	};
+
+	c_list_init (&priv->network_server_lst_head);
+	c_list_init (&priv->process_change_lst_head);
+
+	priv->conn_data_heads = g_hash_table_new_full (_conn_data_head_hash, _conn_data_head_equal, g_free, NULL);
+	priv->conn_data_elems = g_hash_table_new_full (nm_pdirect_hash, nm_pdirect_equal, nm_g_slice_free_fcn (ConnDataElem), NULL);
+
+	priv->bzobjs = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, (GDestroyNotify) _bz_dbus_obj_free, NULL);
+
+	priv->manager = g_object_ref (NM_MANAGER_GET);
 	priv->settings = g_object_ref (NM_SETTINGS_GET);
+	priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
+
+	g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, NULL, &priv->vtable_network_server);
 }
 
 static void
@@ -442,22 +2842,25 @@ dispose (GObject *object)
 	NMBluezManager *self = NM_BLUEZ_MANAGER (object);
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
 
-	if (priv->manager4) {
-		g_signal_handlers_disconnect_by_func (priv->manager4, manager_bdaddr_added_cb, self);
-		g_clear_object (&priv->manager4);
-	}
-	if (priv->manager5) {
-		g_signal_handlers_disconnect_by_data (priv->manager5, self);
-		g_clear_object (&priv->manager5);
-	}
+	/* FIXME(shutdown): we need a nm_device_factory_stop() hook to first unregister all
+	 *   BzDBusObj instances and do necessary cleanup actions (like disconnecting devices
+	 *   or deleting panu_connection). */
 
-	cleanup_checking (self, TRUE);
+	nm_assert (c_list_is_empty (&priv->network_server_lst_head));
+	nm_assert (c_list_is_empty (&priv->process_change_lst_head));
+	nm_assert (priv->process_change_idle_id == 0);
 
-	priv->bluez_version = 0;
+	g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, &priv->vtable_network_server, NULL);
+
+	_cleanup_all (self);
 
 	G_OBJECT_CLASS (nm_bluez_manager_parent_class)->dispose (object);
 
 	g_clear_object (&priv->settings);
+	g_clear_object (&priv->manager);
+	g_clear_object (&priv->dbus_connection);
+
+	nm_clear_pointer (&priv->bzobjs, g_hash_table_destroy);
 }
 
 static void
@@ -466,10 +2869,10 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass);
 
-	object_class->dispose = dispose;
+	object_class->dispose     = dispose;
 
 	factory_class->get_supported_types = get_supported_types;
-	factory_class->create_device = create_device;
-	factory_class->match_connection = match_connection;
-	factory_class->start = start;
+	factory_class->create_device       = create_device;
+	factory_class->match_connection    = match_connection;
+	factory_class->start               = start;
 }
diff --git a/src/devices/bluetooth/nm-bluez-manager.h b/src/devices/bluetooth/nm-bluez-manager.h
new file mode 100644
index 00000000..85dbaa83
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-manager.h
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2009 - 2019 Red Hat, Inc.
+ */
+
+#ifndef __NM_BLUEZ_MANAGER_H__
+#define __NM_BLUEZ_MANAGER_H__
+
+#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))
+#define NM_BLUEZ_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+#define NM_IS_BLUEZ_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
+#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_BLUEZ_MANAGER))
+#define NM_BLUEZ_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+
+typedef struct _NMBluezManager NMBluezManager;
+typedef struct _NMBluezManagerClass NMBluezManagerClass;
+
+GType nm_bluez_manager_get_type (void);
+
+typedef void (*NMBluezManagerConnectCb) (NMBluezManager *self,
+                                         gboolean is_completed /* or else is early notification with DUN path */,
+                                         const char *device_name,
+                                         GError *error,
+                                         gpointer user_data);
+
+gboolean nm_bluez_manager_connect (NMBluezManager *self,
+                                   const char *object_path,
+                                   NMBluetoothCapabilities connection_bt_type,
+                                   int timeout_msec,
+                                   GCancellable *cancellable,
+                                   NMBluezManagerConnectCb callback,
+                                   gpointer callback_user_data,
+                                   GError **error);
+
+void nm_bluez_manager_disconnect (NMBluezManager *self,
+                                  const char *object_path);
+
+#endif /* __NM_BLUEZ_MANAGER_H__ */
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
deleted file mode 100644
index bd230e90..00000000
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ /dev/null
@@ -1,456 +0,0 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2009 - 2012 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez4-adapter.h"
-
-#include "nm-dbus-interface.h"
-#include "nm-bluez-device.h"
-#include "nm-bluez-common.h"
-#include "nm-core-internal.h"
-#include "settings/nm-settings.h"
-
-/*****************************************************************************/
-
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
-	PROP_PATH,
-	PROP_ADDRESS,
-);
-
-enum {
-	INITIALIZED,
-	DEVICE_ADDED,
-	DEVICE_REMOVED,
-	LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
-	char *path;
-	GDBusProxy *proxy;
-	GCancellable *proxy_cancellable;
-	gboolean initialized;
-
-	char *address;
-	GHashTable *devices;
-
-	/* Cached for devices */
-	NMSettings *settings;
-} NMBluez4AdapterPrivate;
-
-struct _NMBluez4Adapter {
-	GObject parent;
-	NMBluez4AdapterPrivate _priv;
-};
-
-struct _NMBluez4AdapterClass {
-	GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluez4Adapter, nm_bluez4_adapter, G_TYPE_OBJECT)
-
-#define NM_BLUEZ4_ADAPTER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluez4Adapter, NM_IS_BLUEZ4_ADAPTER)
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN      LOGD_BT
-#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez4-adapter", __VA_ARGS__)
-
-/*****************************************************************************/
-
-static void device_do_remove (NMBluez4Adapter *self, NMBluezDevice *device);
-
-/*****************************************************************************/
-
-const char *
-nm_bluez4_adapter_get_path (NMBluez4Adapter *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ4_ADAPTER (self), NULL);
-
-	return NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->path;
-}
-
-const char *
-nm_bluez4_adapter_get_address (NMBluez4Adapter *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ4_ADAPTER (self), NULL);
-
-	return NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->address;
-}
-
-gboolean
-nm_bluez4_adapter_get_initialized (NMBluez4Adapter *self)
-{
-	g_return_val_if_fail (NM_IS_BLUEZ4_ADAPTER (self), FALSE);
-
-	return NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->initialized;
-}
-
-GSList *
-nm_bluez4_adapter_get_devices (NMBluez4Adapter *self)
-{
-	GSList *devices = NULL;
-	GHashTableIter iter;
-	NMBluezDevice *device;
-
-	g_hash_table_iter_init (&iter, NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->devices);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
-		if (nm_bluez_device_get_usable (device))
-			devices = g_slist_append (devices, device);
-	}
-	return devices;
-}
-
-static void
-emit_device_removed (NMBluez4Adapter *self, NMBluezDevice *device)
-{
-	_LOGD ("(%s): bluez device now unusable",
-	       nm_bluez_device_get_path (device));
-	g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
-}
-
-static void
-device_usable (NMBluezDevice *device, GParamSpec *pspec, gpointer user_data)
-{
-	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
-
-	if (nm_bluez_device_get_usable (device)) {
-		_LOGD ("(%s): bluez device now usable (device address is %s)",
-		       nm_bluez_device_get_path (device),
-		       nm_bluez_device_get_address (device));
-		g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
-	} else
-		emit_device_removed (self, device);
-}
-
-static void
-device_initialized (NMBluezDevice *device, gboolean success, gpointer user_data)
-{
-	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
-
-	_LOGD ("(%s): bluez device %s",
-	       nm_bluez_device_get_path (device),
-	       success ? "initialized" : "failed to initialize");
-	if (!success)
-		device_do_remove (self, device);
-}
-
-static void
-device_do_remove (NMBluez4Adapter *self, NMBluezDevice *device)
-{
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-
-	if (g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device))) {
-		g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_initialized), self);
-		g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_usable), self);
-
-		if (nm_bluez_device_get_usable (device))
-			emit_device_removed (self, device);
-
-		g_object_unref (device);
-	}
-}
-
-static void
-device_created (GDBusProxy *proxy, const char *path, gpointer user_data)
-{
-	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-	NMBluezDevice *device;
-
-	device = nm_bluez_device_new (path, priv->address, priv->settings, 4);
-	g_signal_connect (device, NM_BLUEZ_DEVICE_INITIALIZED, G_CALLBACK (device_initialized), self);
-	g_signal_connect (device, "notify::" NM_BLUEZ_DEVICE_USABLE, G_CALLBACK (device_usable), self);
-	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
-
-	_LOGD ("(%s): new bluez device found", path);
-}
-
-static void
-device_removed (GDBusProxy *proxy, const char *path, gpointer user_data)
-{
-	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-	NMBluezDevice *device;
-
-	_LOGD ("(%s): bluez device removed", path);
-
-	device = g_hash_table_lookup (priv->devices, path);
-	if (device)
-		device_do_remove (self, device);
-}
-
-static void
-get_properties_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
-{
-	NMBluez4Adapter *self;
-	NMBluez4AdapterPrivate *priv;
-	gs_free_error GError *error = NULL;
-	GVariant *ret, *properties;
-	char **devices;
-	int i;
-
-	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
-	                                  G_VARIANT_TYPE ("(a{sv})"), &error);
-
-	if (   !ret
-	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
-
-	self = NM_BLUEZ4_ADAPTER (user_data);
-	priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-
-	g_clear_object (&priv->proxy_cancellable);
-
-	if (!ret) {
-		g_dbus_error_strip_remote_error (error);
-		_LOGW ("bluez error getting adapter properties: %s", error->message);
-		goto done;
-	}
-
-	properties = g_variant_get_child_value (ret, 0);
-
-	(void) g_variant_lookup (properties, "Address", "s", &priv->address);
-	if (g_variant_lookup (properties, "Devices", "^ao", &devices)) {
-		for (i = 0; devices[i]; i++)
-			device_created (priv->proxy, devices[i], self);
-		g_strfreev (devices);
-	}
-
-	g_variant_unref (properties);
-	g_variant_unref (ret);
-
-	priv->initialized = TRUE;
-
-done:
-	g_signal_emit (self, signals[INITIALIZED], 0, priv->initialized);
-}
-
-static void
-_proxy_new_cb (GObject *source_object,
-               GAsyncResult *result,
-               gpointer user_data)
-{
-	NMBluez4Adapter *self;
-	NMBluez4AdapterPrivate *priv;
-	gs_free_error GError *error = NULL;
-	GDBusProxy *proxy;
-
-	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
-	if (   !proxy
-	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
-
-	self = user_data;
-	priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-
-	if (!proxy) {
-		_LOGW ("bluez error creating D-Bus proxy: %s", error->message);
-		g_clear_object (&priv->proxy_cancellable);
-		g_signal_emit (self, signals[INITIALIZED], 0, priv->initialized);
-		return;
-	}
-
-	priv->proxy = proxy;
-
-	_nm_dbus_signal_connect (priv->proxy, "DeviceCreated", G_VARIANT_TYPE ("(o)"),
-	                         G_CALLBACK (device_created), self);
-	_nm_dbus_signal_connect (priv->proxy, "DeviceRemoved", G_VARIANT_TYPE ("(o)"),
-	                         G_CALLBACK (device_removed), self);
-
-	g_dbus_proxy_call (priv->proxy, "GetProperties",
-	                   NULL,
-	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   priv->proxy_cancellable,
-	                   get_properties_cb,
-	                   self);
-}
-
-/*****************************************************************************/
-
-static gboolean
-_find_all (gpointer key, gpointer value, gpointer user_data)
-{
-	return TRUE;
-}
-
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE ((NMBluez4Adapter *) object);
-
-	switch (prop_id) {
-	case PROP_PATH:
-		g_value_set_string (value, priv->path);
-		break;
-	case PROP_ADDRESS:
-		g_value_set_string (value, priv->address);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
-{
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE ((NMBluez4Adapter *) object);
-
-	switch (prop_id) {
-	case PROP_PATH:
-		/* construct-only */
-		priv->path = g_value_dup_string (value);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez4_adapter_init (NMBluez4Adapter *self)
-{
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-
-	priv->devices = g_hash_table_new_full (nm_str_hash, g_str_equal,
-	                                       NULL, NULL);
-}
-
-NMBluez4Adapter *
-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->settings = g_object_ref (settings);
-
-	priv->proxy_cancellable = g_cancellable_new ();
-
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
-	                          NULL,
-	                          NM_BLUEZ_SERVICE,
-	                          priv->path,
-	                          NM_BLUEZ4_ADAPTER_INTERFACE,
-	                          priv->proxy_cancellable,
-	                          _proxy_new_cb,
-	                          self);
-	return self;
-}
-
-static void
-dispose (GObject *object)
-{
-	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (object);
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-	NMBluezDevice *device;
-
-	nm_clear_g_cancellable (&priv->proxy_cancellable);
-
-	while ((device = g_hash_table_find (priv->devices, _find_all, NULL)))
-		device_do_remove (self, device);
-
-	if (priv->proxy) {
-		g_signal_handlers_disconnect_by_data (priv->proxy, self);
-		g_clear_object (&priv->proxy);
-	}
-
-	G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->dispose (object);
-}
-
-static void
-finalize (GObject *object)
-{
-	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (object);
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-
-	g_hash_table_destroy (priv->devices);
-	g_free (priv->address);
-	g_free (priv->path);
-
-	G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->finalize (object);
-
-	g_object_unref (priv->settings);
-}
-
-static void
-nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-
-	object_class->get_property = get_property;
-	object_class->set_property = set_property;
-	object_class->dispose = dispose;
-	object_class->finalize = finalize;
-
-	obj_properties[PROP_PATH] =
-	     g_param_spec_string (NM_BLUEZ4_ADAPTER_PATH, "", "",
-	                          NULL,
-	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-	                          G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_ADDRESS] =
-	     g_param_spec_string (NM_BLUEZ4_ADAPTER_ADDRESS, "", "",
-	                          NULL,
-	                          G_PARAM_READABLE |
-	                          G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
-	signals[INITIALIZED] = g_signal_new (NM_BLUEZ4_ADAPTER_INITIALIZED,
-	                                     G_OBJECT_CLASS_TYPE (object_class),
-	                                     G_SIGNAL_RUN_LAST,
-	                                     0,
-	                                     NULL, NULL,
-	                                     g_cclosure_marshal_VOID__BOOLEAN,
-	                                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
-	signals[DEVICE_ADDED] = g_signal_new (NM_BLUEZ4_ADAPTER_DEVICE_ADDED,
-	                                      G_OBJECT_CLASS_TYPE (object_class),
-	                                      G_SIGNAL_RUN_LAST,
-	                                      0,
-	                                      NULL, NULL,
-	                                      g_cclosure_marshal_VOID__OBJECT,
-	                                      G_TYPE_NONE, 1, G_TYPE_OBJECT);
-
-	signals[DEVICE_REMOVED] = g_signal_new (NM_BLUEZ4_ADAPTER_DEVICE_REMOVED,
-	                                        G_OBJECT_CLASS_TYPE (object_class),
-	                                        G_SIGNAL_RUN_LAST,
-	                                        0,
-	                                        NULL, NULL,
-	                                        g_cclosure_marshal_VOID__OBJECT,
-	                                        G_TYPE_NONE, 1, G_TYPE_OBJECT);
-}
-
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h
deleted file mode 100644
index 82bd2de8..00000000
--- a/src/devices/bluetooth/nm-bluez4-adapter.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2009 - 2012 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
-#define __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
-
-#include "nm-bluez-device.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))
-#define NM_BLUEZ4_ADAPTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4AdapterClass))
-#define NM_IS_BLUEZ4_ADAPTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ4_ADAPTER))
-#define NM_IS_BLUEZ4_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ4_ADAPTER))
-#define NM_BLUEZ4_ADAPTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4AdapterClass))
-
-/* Properties */
-#define NM_BLUEZ4_ADAPTER_PATH    "path"
-#define NM_BLUEZ4_ADAPTER_ADDRESS "address"
-
-/* Signals */
-#define NM_BLUEZ4_ADAPTER_INITIALIZED      "initialized"
-#define NM_BLUEZ4_ADAPTER_DEVICE_ADDED     "device-added"
-#define NM_BLUEZ4_ADAPTER_DEVICE_REMOVED   "device-removed"
-
-typedef struct _NMBluez4Adapter NMBluez4Adapter;
-typedef struct _NMBluez4AdapterClass NMBluez4AdapterClass;
-
-GType nm_bluez4_adapter_get_type (void);
-
-NMBluez4Adapter *nm_bluez4_adapter_new (const char *path,
-                                        NMSettings *settings);
-
-const char *nm_bluez4_adapter_get_path (NMBluez4Adapter *self);
-
-const char *nm_bluez4_adapter_get_address (NMBluez4Adapter *self);
-
-gboolean nm_bluez4_adapter_get_initialized (NMBluez4Adapter *self);
-
-GSList *nm_bluez4_adapter_get_devices (NMBluez4Adapter *self);
-
-#endif /* __NETWORKMANAGER_BLUEZ4_ADAPTER_H__ */
diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c
deleted file mode 100644
index 8327776d..00000000
--- a/src/devices/bluetooth/nm-bluez4-manager.c
+++ /dev/null
@@ -1,355 +0,0 @@
-/* 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) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2013 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez4-manager.h"
-
-#include <signal.h>
-#include <stdlib.h>
-
-#include "nm-bluez4-adapter.h"
-#include "nm-bluez-common.h"
-#include "nm-core-internal.h"
-#include "settings/nm-settings.h"
-
-/*****************************************************************************/
-
-enum {
-	BDADDR_ADDED,
-	LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
-	gulong name_owner_changed_id;
-
-	NMSettings *settings;
-
-	GDBusProxy *proxy;
-	GCancellable *proxy_cancellable;
-
-	NMBluez4Adapter *adapter;
-} NMBluez4ManagerPrivate;
-
-struct _NMBluez4Manager {
-	GObject parent;
-	NMBluez4ManagerPrivate _priv;
-};
-
-struct _NMBluez4ManagerClass {
-	GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluez4Manager, nm_bluez4_manager, G_TYPE_OBJECT)
-
-#define NM_BLUEZ4_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluez4Manager, NM_IS_BLUEZ4_MANAGER)
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN      LOGD_BT
-#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez4-manager", __VA_ARGS__)
-
-/*****************************************************************************/
-
-static void
-emit_bdaddr_added (NMBluez4Manager *self, NMBluezDevice *device)
-{
-	g_signal_emit (self, signals[BDADDR_ADDED], 0,
-	               device,
-	               nm_bluez_device_get_address (device),
-	               nm_bluez_device_get_name (device),
-	               nm_bluez_device_get_path (device),
-	               nm_bluez_device_get_capabilities (device));
-}
-
-void
-nm_bluez4_manager_query_devices (NMBluez4Manager *self)
-{
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	GSList *devices, *iter;
-
-	if (!priv->adapter)
-		return;
-
-	devices = nm_bluez4_adapter_get_devices (priv->adapter);
-	for (iter = devices; iter; iter = g_slist_next (iter))
-		emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
-	g_slist_free (devices);
-}
-
-static void
-device_added (NMBluez4Adapter *adapter, NMBluezDevice *device, gpointer user_data)
-{
-	emit_bdaddr_added (NM_BLUEZ4_MANAGER (user_data), device);
-}
-
-static void
-device_removed (NMBluez4Adapter *adapter, NMBluezDevice *device, gpointer user_data)
-{
-	/* Re-emit the signal on the device for now; flatten this later */
-	g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
-}
-
-static void
-adapter_initialized (NMBluez4Adapter *adapter, gboolean success, gpointer user_data)
-{
-	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-
-	if (success) {
-		GSList *devices, *iter;
-
-		devices = nm_bluez4_adapter_get_devices (adapter);
-		for (iter = devices; iter; iter = g_slist_next (iter))
-			emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
-		g_slist_free (devices);
-
-		g_signal_connect (adapter, NM_BLUEZ4_ADAPTER_DEVICE_ADDED,
-		                  G_CALLBACK (device_added), self);
-		g_signal_connect (adapter, NM_BLUEZ4_ADAPTER_DEVICE_REMOVED,
-		                  G_CALLBACK (device_removed), self);
-	} else {
-		g_object_unref (priv->adapter);
-		priv->adapter = NULL;
-	}
-}
-
-static void
-adapter_removed (GDBusProxy *proxy, const char *path, NMBluez4Manager *self)
-{
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-
-	if (priv->adapter && !strcmp (path, nm_bluez4_adapter_get_path (priv->adapter))) {
-		if (nm_bluez4_adapter_get_initialized (priv->adapter)) {
-			GSList *devices, *iter;
-
-			devices = nm_bluez4_adapter_get_devices (priv->adapter);
-			for (iter = devices; iter; iter = g_slist_next (iter))
-				g_signal_emit_by_name (NM_BLUEZ_DEVICE (iter->data), NM_BLUEZ_DEVICE_REMOVED);
-			g_slist_free (devices);
-		}
-
-		g_object_unref (priv->adapter);
-		priv->adapter = NULL;
-	}
-}
-
-static void
-default_adapter_changed (GDBusProxy *proxy, const char *path, NMBluez4Manager *self)
-{
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	const char *cur_path = NULL;
-
-	if (priv->adapter)
-		cur_path = nm_bluez4_adapter_get_path (priv->adapter);
-
-	if (cur_path) {
-		if (!path || strcmp (path, cur_path)) {
-			/* Default adapter changed */
-			adapter_removed (priv->proxy, cur_path, self);
-		} else {
-			/* This adapter is already the default */
-			return;
-		}
-	}
-
-	/* Add the new default adapter */
-	if (path) {
-		priv->adapter = nm_bluez4_adapter_new (path, priv->settings);
-		g_signal_connect (priv->adapter, NM_BLUEZ4_ADAPTER_INITIALIZED,
-		                  G_CALLBACK (adapter_initialized), self);
-	}
-}
-
-static void
-default_adapter_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
-{
-	NMBluez4Manager *self;
-	NMBluez4ManagerPrivate *priv;
-	gs_unref_variant GVariant *ret = NULL;
-	gs_free_error GError *error = NULL;
-	const char *default_adapter;
-
-	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
-	                                  G_VARIANT_TYPE ("(o)"), &error);
-	if (   !ret
-	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
-
-	self = NM_BLUEZ4_MANAGER (user_data);
-	priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-
-	g_clear_object (&priv->proxy_cancellable);
-
-	if (!ret) {
-		/* Ignore "No such adapter" errors; just means bluetooth isn't active */
-		if (   !_nm_dbus_error_has_name (error, "org.bluez.Error.NoSuchAdapter")
-		    && !_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")
-		    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
-			g_dbus_error_strip_remote_error (error);
-			_LOGW ("bluez error getting default adapter: %s",
-			       error->message);
-		}
-		return;
-	}
-
-	g_variant_get (ret, "(&o)", &default_adapter);
-	default_adapter_changed (priv->proxy, default_adapter, self);
-}
-
-static void
-name_owner_changed (NMBluez4Manager *self)
-{
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	gs_free char *owner = NULL;
-
-	nm_clear_g_cancellable (&priv->proxy_cancellable);
-
-	owner = g_dbus_proxy_get_name_owner (priv->proxy);
-	if (!owner) {
-		/* Throwing away the adapter removes all devices too */
-		g_clear_object (&priv->adapter);
-		return;
-	}
-
-	priv->proxy_cancellable = g_cancellable_new ();
-
-	g_dbus_proxy_call (priv->proxy, "DefaultAdapter",
-	                   NULL,
-	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   priv->proxy_cancellable,
-	                   default_adapter_cb,
-	                   self);
-}
-
-static void
-name_owner_changed_cb (GObject *object,
-                       GParamSpec *pspec,
-                       gpointer user_data)
-{
-	name_owner_changed (user_data);
-}
-
-static void
-_proxy_new_cb (GObject *source_object,
-               GAsyncResult *result,
-               gpointer user_data)
-{
-	NMBluez4Manager *self;
-	NMBluez4ManagerPrivate *priv;
-	gs_free_error GError *error = NULL;
-	GDBusProxy *proxy;
-
-	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
-	if (   !proxy
-	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
-
-	self = user_data;
-	priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-
-	if (!proxy) {
-		_LOGW ("bluez error creating D-Bus proxy: %s", error->message);
-		g_clear_object (&priv->proxy_cancellable);
-		return;
-	}
-
-	priv->proxy = proxy;
-
-	_nm_dbus_signal_connect (priv->proxy, "AdapterRemoved", G_VARIANT_TYPE ("(o)"),
-	                         G_CALLBACK (adapter_removed), self);
-	_nm_dbus_signal_connect (priv->proxy, "DefaultAdapterChanged", G_VARIANT_TYPE ("(o)"),
-	                         G_CALLBACK (default_adapter_changed), self);
-	g_signal_connect (priv->proxy, "notify::g-name-owner",
-	                  G_CALLBACK (name_owner_changed_cb), self);
-
-	name_owner_changed (self);
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez4_manager_init (NMBluez4Manager *self)
-{
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-
-	priv->proxy_cancellable = g_cancellable_new ();
-
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
-	                          NULL,
-	                          NM_BLUEZ_SERVICE,
-	                          NM_BLUEZ_MANAGER_PATH,
-	                          NM_BLUEZ4_MANAGER_INTERFACE,
-	                          priv->proxy_cancellable,
-	                          _proxy_new_cb,
-	                          self);
-}
-
-NMBluez4Manager *
-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)->settings = g_object_ref (settings);
-	return instance;
-}
-
-static void
-dispose (GObject *object)
-{
-	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (object);
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-
-	nm_clear_g_cancellable (&priv->proxy_cancellable);
-
-	if (priv->proxy) {
-		g_signal_handlers_disconnect_by_data (priv->proxy, self);
-		g_clear_object (&priv->proxy);
-	}
-
-	g_clear_object (&priv->adapter);
-
-	G_OBJECT_CLASS (nm_bluez4_manager_parent_class)->dispose (object);
-
-	g_clear_object (&priv->settings);
-}
-
-static void
-nm_bluez4_manager_class_init (NMBluez4ManagerClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	object_class->dispose = dispose;
-
-	signals[BDADDR_ADDED] =
-	    g_signal_new (NM_BLUEZ_MANAGER_BDADDR_ADDED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
-	                  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
-}
-
diff --git a/src/devices/bluetooth/nm-bluez4-manager.h b/src/devices/bluetooth/nm-bluez4-manager.h
deleted file mode 100644
index f46379ff..00000000
--- a/src/devices/bluetooth/nm-bluez4-manager.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* 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) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2013 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ4_MANAGER_H__
-#define __NETWORKMANAGER_BLUEZ4_MANAGER_H__
-
-#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))
-#define NM_IS_BLUEZ4_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ4_MANAGER))
-#define NM_IS_BLUEZ4_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ4_MANAGER))
-#define NM_BLUEZ4_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ4_MANAGER, NMBluez4ManagerClass))
-
-typedef struct _NMBluez4Manager NMBluez4Manager;
-typedef struct _NMBluez4ManagerClass NMBluez4ManagerClass;
-
-GType nm_bluez4_manager_get_type (void);
-
-NMBluez4Manager *nm_bluez4_manager_new (NMSettings *settings);
-
-void nm_bluez4_manager_query_devices (NMBluez4Manager *manager);
-
-#endif /* __NETWORKMANAGER_BLUEZ4_MANAGER_H__ */
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
index b9a1fa0a..af463d1a 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.c
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -32,145 +18,407 @@
 #include "nm-bt-error.h"
 #include "NetworkManagerUtils.h"
 
+#define RFCOMM_FMT "/dev/rfcomm%d"
+
+/*****************************************************************************/
+
+typedef struct {
+	GCancellable *cancellable;
+	NMBluez5DunConnectCb callback;
+	gpointer callback_user_data;
+
+	sdp_session_t *sdp_session;
+
+	GError *rfcomm_sdp_search_error;
+
+	gint64 connect_open_tty_started_at;
+
+	gulong cancelled_id;
+
+	guint source_id;
+
+	guint8 sdp_session_try_count;
+} ConnectData;
+
 struct _NMBluez5DunContext {
+	const char *dst_str;
+
+	ConnectData *cdat;
+
+	NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb;
+	gpointer notify_tty_hangup_user_data;
+
+	char *rfcomm_tty_path;
+
+	int rfcomm_sock_fd;
+	int rfcomm_tty_fd;
+	int rfcomm_tty_no;
+	int rfcomm_channel;
+
+	guint rfcomm_tty_poll_id;
+
 	bdaddr_t src;
 	bdaddr_t dst;
-	char *src_str;
-	char *dst_str;
-	int rfcomm_channel;
-	int rfcomm_fd;
-	int rfcomm_tty_fd;
-	int rfcomm_id;
-	NMBluez5DunFunc callback;
-	gpointer user_data;
-	sdp_session_t *sdp_session;
-	guint sdp_watch_id;
+
+	char src_str[];
 };
 
-static void
-dun_connect (NMBluez5DunContext *context)
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN      LOGD_BT
+#define _NMLOG_PREFIX_NAME "bluez"
+#define _NMLOG(level, context, ...) \
+    G_STMT_START { \
+        if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
+            const NMBluez5DunContext *const _context = (context); \
+            \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
+                     "%s: DUN[%s] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     _context->src_str \
+                     _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/*****************************************************************************/
+
+static void _context_invoke_callback_success (NMBluez5DunContext *context);
+static void _context_invoke_callback_fail_and_free (NMBluez5DunContext *context,
+                                                    GError *error);
+static void _context_free (NMBluez5DunContext *context);
+static int _connect_open_tty (NMBluez5DunContext *context);
+static gboolean _connect_sdp_session_start (NMBluez5DunContext *context,
+                                            GError **error);
+
+/*****************************************************************************/
+
+NM_AUTO_DEFINE_FCN0 (NMBluez5DunContext *, _nm_auto_free_context, _context_free)
+#define nm_auto_free_context nm_auto(_nm_auto_free_context)
+
+/*****************************************************************************/
+
+const char *
+nm_bluez5_dun_context_get_adapter (const NMBluez5DunContext *context)
 {
-	struct sockaddr_rc sa;
-	int devid, try = 30;
-	char tty[100];
-	const int ttylen = sizeof (tty) - 1;
-	GError *error = NULL;
-	int errsv;
+	return context->src_str;
+}
 
-	struct rfcomm_dev_req req = {
-		.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
-		.dev_id = -1,
-		.channel = context->rfcomm_channel
-	};
+const char *
+nm_bluez5_dun_context_get_remote (const NMBluez5DunContext *context)
+{
+	return context->dst_str;
+}
 
-	context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC, BTPROTO_RFCOMM);
-	if (context->rfcomm_fd < 0) {
-		errsv = errno;
-		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Failed to create RFCOMM socket: (%d) %s",
-		                     errsv, nm_strerror_native (errsv));
-		goto done;
-	}
+const char *
+nm_bluez5_dun_context_get_rfcomm_dev (const NMBluez5DunContext *context)
+{
+	return context->rfcomm_tty_path;
+}
 
-	/* Connect to the remote device */
-	sa.rc_family = AF_BLUETOOTH;
-	sa.rc_channel = 0;
-	memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
-	if (bind (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof(sa))) {
-		errsv = errno;
-		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Failed to bind socket: (%d) %s",
-		                     errsv, nm_strerror_native (errsv));
-		goto done;
+/*****************************************************************************/
+
+static gboolean
+_rfcomm_tty_poll_cb (GIOChannel *stream,
+                     GIOCondition condition,
+                     gpointer user_data)
+{
+	NMBluez5DunContext *context = user_data;
+
+	_LOGD (context, "receive %s%s%s signal on rfcomm file descriptor",
+	       NM_FLAGS_HAS (condition, G_IO_ERR)            ? "ERR" : "",
+	       NM_FLAGS_ALL (condition, G_IO_HUP | G_IO_ERR) ? ","   : "",
+	       NM_FLAGS_HAS (condition, G_IO_HUP)            ? "HUP" : "");
+
+	context->rfcomm_tty_poll_id = 0;
+	context->notify_tty_hangup_cb (context,
+	                               context->notify_tty_hangup_user_data);
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_open_tty_retry_cb (gpointer user_data)
+{
+	NMBluez5DunContext *context = user_data;
+	int r;
+
+	r = _connect_open_tty (context);
+	if (r >= 0)
+		return G_SOURCE_REMOVE;
+
+	if (nm_utils_get_monotonic_timestamp_ns () > context->cdat->connect_open_tty_started_at + (30 * 100 * NM_UTILS_NS_PER_MSEC)) {
+		gs_free_error GError *error = NULL;
+
+		context->cdat->source_id = 0;
+		g_set_error (&error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "give up waiting to open %s device: %s (%d)",
+		             context->rfcomm_tty_path,
+		             nm_strerror_native (r),
+		             -r);
+		_context_invoke_callback_fail_and_free (context, error);
+		return G_SOURCE_REMOVE;
 	}
 
-	sa.rc_channel = context->rfcomm_channel;
-	memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
-	if (connect (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof (sa)) ) {
-		errsv = errno;
-		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Failed to connect to remote device: (%d) %s",
-		                     errsv, nm_strerror_native (errsv));
-		goto done;
+	return G_SOURCE_CONTINUE;
+}
+
+static int
+_connect_open_tty (NMBluez5DunContext *context)
+{
+	nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
+	int fd;
+	int errsv;
+
+	fd = open (context->rfcomm_tty_path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+	if (fd < 0) {
+		errsv = NM_ERRNO_NATIVE (errno);
+
+		if (context->cdat->source_id == 0) {
+			_LOGD (context, "failed opening tty "RFCOMM_FMT": %s (%d). Start polling...",
+			       context->rfcomm_tty_no,
+			       nm_strerror_native (errsv),
+			       errsv);
+			context->cdat->connect_open_tty_started_at = nm_utils_get_monotonic_timestamp_ns ();
+			context->cdat->source_id = g_timeout_add (100,
+			                                          _connect_open_tty_retry_cb,
+			                                          context);
+		}
+		return -errsv;
 	}
 
-	nm_log_dbg (LOGD_BT, "(%s): connected to %s on channel %d",
-	            context->src_str, context->dst_str, context->rfcomm_channel);
+	context->rfcomm_tty_fd = fd;
+
+	io_channel = g_io_channel_unix_new (context->rfcomm_tty_fd);
+	context->rfcomm_tty_poll_id = g_io_add_watch (io_channel,
+	                                              G_IO_ERR | G_IO_HUP,
+	                                              _rfcomm_tty_poll_cb,
+	                                              context);
+
+	_context_invoke_callback_success (context);
+	return 0;
+}
+
+static void
+_connect_create_rfcomm (NMBluez5DunContext *context)
+{
+	gs_free_error GError *error = NULL;
+	struct rfcomm_dev_req req;
+	int devid;
+	int errsv;
+	int r;
+
+	_LOGD (context, "connected to %s on channel %d",
+	       context->dst_str, context->rfcomm_channel);
 
 	/* Create an RFCOMM kernel device for the DUN channel */
+	memset (&req, 0, sizeof (req));
+	req.dev_id  = -1;
+	req.flags   = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+	req.channel = context->rfcomm_channel;
 	memcpy (&req.src, &context->src, ETH_ALEN);
 	memcpy (&req.dst, &context->dst, ETH_ALEN);
-	devid = ioctl (context->rfcomm_fd, RFCOMMCREATEDEV, &req);
+	devid = ioctl (context->rfcomm_sock_fd, RFCOMMCREATEDEV, &req);
 	if (devid < 0) {
-		errsv = errno;
-		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Failed to create rfcomm device: (%d) %s",
-		                     errsv, nm_strerror_native (errsv));
-		goto done;
+		errsv = NM_ERRNO_NATIVE (errno);
+		if (errsv == EBADFD) {
+			/* hm. We use a non-blocking socket to connect. Above getsockopt(SOL_SOCKET,SO_ERROR) indicated
+			 * success, but still now we fail with EBADFD. I think that is a bug and we should get the
+			 * failure during connect().
+			 *
+			 * Anyway, craft a less confusing error message than
+			 * "failed to create rfcomm device: File descriptor in bad state (77)". */
+			g_set_error (&error,
+			             NM_BT_ERROR,
+			             NM_BT_ERROR_DUN_CONNECT_FAILED,
+			             "unknown failure to connect to DUN device");
+		} else {
+			g_set_error (&error,
+			             NM_BT_ERROR,
+			             NM_BT_ERROR_DUN_CONNECT_FAILED,
+			             "failed to create rfcomm device: %s (%d)",
+			             nm_strerror_native (errsv), errsv);
+		}
+		_context_invoke_callback_fail_and_free (context, error);
+		return;
 	}
-	context->rfcomm_id = devid;
 
-	snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
-	while ((context->rfcomm_tty_fd = open (tty, O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0 && try--) {
-		if (try) {
-			g_usleep (100 * 1000);
-			continue;
-		}
+	context->rfcomm_tty_no = devid;
+	context->rfcomm_tty_path = g_strdup_printf (RFCOMM_FMT, devid);
+
+	r = _connect_open_tty (context);
+	if (r < 0) {
+		/* we created the rfcomm device, but cannot yet open it. That means, we are
+		 * not yet fully connected. However, we notify the caller about "what we learned
+		 * so far". Note that this happens synchronously.
+		 *
+		 * The purpose is that once we proceed synchrnously, modem-manager races with
+		 * the detection of the modem. We want to notify the caller first about the
+		 * device name. */
+		context->cdat->callback (NULL,
+		                         context->rfcomm_tty_path,
+		                         NULL,
+		                         context->cdat->callback_user_data);
+	}
+}
 
-		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Failed to find rfcomm device: %s",
-		                     tty);
-		break;
+static gboolean
+_connect_socket_connect_cb (GIOChannel *stream,
+                            GIOCondition condition,
+                            gpointer user_data)
+{
+	NMBluez5DunContext *context = user_data;
+	gs_free_error GError *error = NULL;
+	int errsv = 0;
+	socklen_t slen = sizeof(errsv);
+	int r;
+
+	context->cdat->source_id = 0;
+
+	r = getsockopt (context->rfcomm_sock_fd, SOL_SOCKET, SO_ERROR, &errsv, &slen);
+
+	if (r < 0) {
+		errsv = errno;
+		g_set_error (&error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "failed to complete connecting RFCOMM socket: %s (%d)",
+		             nm_strerror_native (errsv), errsv);
+		_context_invoke_callback_fail_and_free (context, error);
+		return G_SOURCE_REMOVE;
 	}
 
-done:
-	context->callback (context, tty, error, context->user_data);
+	if (errsv != 0) {
+		g_set_error (&error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "failed to connect RFCOMM socket: %s (%d)",
+		             nm_strerror_native (errsv), errsv);
+		_context_invoke_callback_fail_and_free (context, error);
+		return G_SOURCE_REMOVE;
+	}
+
+	_connect_create_rfcomm (context);
+	return G_SOURCE_REMOVE;
 }
 
 static void
-sdp_search_cleanup (NMBluez5DunContext *context)
+_connect_socket_connect (NMBluez5DunContext *context)
 {
-	if (context->sdp_session) {
-		sdp_close (context->sdp_session);
-		context->sdp_session = NULL;
+	gs_free_error GError *error = NULL;
+	struct sockaddr_rc sa;
+	int errsv;
+
+	context->rfcomm_sock_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_RFCOMM);
+	if (context->rfcomm_sock_fd < 0) {
+		errsv = errno;
+		g_set_error (&error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "failed to create RFCOMM socket: %s (%d)",
+		             nm_strerror_native (errsv), errsv);
+		_context_invoke_callback_fail_and_free (context, error);
+		return;
+	}
+
+	/* Connect to the remote device */
+	memset (&sa, 0, sizeof (sa));
+	sa.rc_family = AF_BLUETOOTH;
+	sa.rc_channel = 0;
+	memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
+	if (bind (context->rfcomm_sock_fd,
+	          (struct sockaddr *) &sa,
+	          sizeof(sa)) != 0) {
+		errsv = errno;
+		g_set_error (&error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "failed to bind socket: %s (%d)",
+		             nm_strerror_native (errsv), errsv);
+		_context_invoke_callback_fail_and_free (context, error);
+		return;
+	}
+
+	memset (&sa, 0, sizeof (sa));
+	sa.rc_family = AF_BLUETOOTH;
+	sa.rc_channel = context->rfcomm_channel;
+	memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
+	if (connect (context->rfcomm_sock_fd,
+	             (struct sockaddr *) &sa,
+	             sizeof (sa)) != 0) {
+		nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
+
+		errsv = errno;
+		if (errsv != EINPROGRESS) {
+			g_set_error (&error,
+			             NM_BT_ERROR,
+			             NM_BT_ERROR_DUN_CONNECT_FAILED,
+			             "failed to connect to remote device: %s (%d)",
+			             nm_strerror_native (errsv), errsv);
+			_context_invoke_callback_fail_and_free (context, error);
+			return;
+		}
+
+		_LOGD (context, "connecting to %s on channel %d...",
+		       context->dst_str,
+		       context->rfcomm_channel);
+
+		io_channel = g_io_channel_unix_new (context->rfcomm_sock_fd);
+		context->cdat->source_id = g_io_add_watch (io_channel,
+		                                           G_IO_OUT,
+		                                           _connect_socket_connect_cb,
+		                                           context);
+		return;
 	}
 
-	nm_clear_g_source (&context->sdp_watch_id);
+	_connect_create_rfcomm (context);
 }
 
 static void
-sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data)
+_connect_sdp_search_cb (uint8_t type,
+                        uint16_t status,
+                        uint8_t *rsp,
+                        size_t size,
+                        void *user_data)
 {
 	NMBluez5DunContext *context = user_data;
-	int scanned, seqlen = 0, bytesleft = size;
+	int scanned;
+	int seqlen = 0;
+	int bytesleft = size;
 	uint8_t dataType;
 	int channel = -1;
 
-	nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished with type=%d status=%d",
-	            context->src_str, context->dst_str, status, type);
+	if (   context->cdat->rfcomm_sdp_search_error
+	    || context->rfcomm_channel >= 0)
+		return;
+
+	_LOGD (context, "SDP search finished with type=%d status=%d",
+	       status, type);
 
 	/* SDP response received */
-	if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
-		GError *error = g_error_new (NM_BT_ERROR,
-		                             NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                             "Did not get a Service Discovery response");
-		context->callback (context, NULL, error, context->user_data);
-		goto done;
+	if (   status
+	    || type != SDP_SVC_SEARCH_ATTR_RSP) {
+		g_set_error (&context->cdat->rfcomm_sdp_search_error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "did not get a Service Discovery response");
+		return;
 	}
 
 	scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
 
-	nm_log_dbg (LOGD_BT, "(%s -> %s): SDP sequence type scanned=%d length=%d",
-	            context->src_str, context->dst_str, scanned, seqlen);
+	_LOGD (context, "SDP sequence type scanned=%d length=%d",
+	       scanned, seqlen);
 
 	scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
-	if (!scanned || !seqlen) {
+	if (   !scanned
+	    || !seqlen) {
 		/* Short read or unknown sequence type */
-		GError *error = g_error_new (NM_BT_ERROR,
-		                             NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                             "Improper Service Discovery response");
-		context->callback (context, NULL, error, context->user_data);
-		goto done;
+		g_set_error (&context->cdat->rfcomm_sdp_search_error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "improper Service Discovery response");
+		return;
 	}
 
 	rsp += scanned;
@@ -194,90 +442,153 @@ sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t siz
 			channel = sdp_get_proto_port (protos, RFCOMM_UUID);
 			sdp_list_free (protos, NULL);
 
-			nm_log_dbg (LOGD_BT, "(%s -> %s): SDP channel=%d",
-			            context->src_str, context->dst_str, channel);
+			_LOGD (context, "SDP channel=%d",
+			       channel);
 		}
 		sdp_record_free (rec);
 
 		scanned += recsize;
 		rsp += recsize;
 		bytesleft -= recsize;
-	} while ((scanned < (ssize_t) size) && (bytesleft > 0) && (channel < 0));
-
-done:
-	if (channel != -1) {
-		context->rfcomm_channel = channel;
-		dun_connect (context);
+	} while (   scanned < (ssize_t) size
+	         && bytesleft > 0
+	         && channel < 0);
+
+	if (channel == -1) {
+		g_set_error (&context->cdat->rfcomm_sdp_search_error,
+		             NM_BT_ERROR,
+		             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "did not receive rfcomm-channel");
+		return;
 	}
 
-	sdp_search_cleanup (context);
+	context->rfcomm_channel = channel;
 }
 
 static gboolean
-sdp_search_process_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+_connect_sdp_search_io_cb (GIOChannel *io_channel,
+                           GIOCondition condition,
+                           gpointer user_data)
 {
 	NMBluez5DunContext *context = user_data;
-
-	nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search progressed with condition=%d",
-	            context->src_str, context->dst_str, condition);
+	gs_free_error GError *error = NULL;
+	int errsv;
 
 	if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
-		GError *error = g_error_new (NM_BT_ERROR,
-		                             NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                             "Service Discovery interrupted");
-		context->callback (context, NULL, error, context->user_data);
-		sdp_search_cleanup (context);
-		return FALSE;
+		_LOGD (context, "SDP search returned with invalid IO condition 0x%x",
+		       (guint) condition);
+		error = g_error_new (NM_BT_ERROR,
+		                     NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Service Discovery interrupted");
+		context->cdat->source_id = 0;
+		_context_invoke_callback_fail_and_free (context, error);
+		return G_SOURCE_REMOVE;
 	}
 
-	if (sdp_process (context->sdp_session) < 0) {
-		nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished",
-		            context->src_str, context->dst_str);
+	if (sdp_process (context->cdat->sdp_session) == 0) {
+		_LOGD (context, "SDP search still not finished");
+		return G_SOURCE_CONTINUE;
+	}
 
-		/* Search finished successfully. */
-		return FALSE;
+	context->cdat->source_id = 0;
+
+	if (   context->rfcomm_channel < 0
+	    && !context->cdat->rfcomm_sdp_search_error) {
+		errsv = sdp_get_error (context->cdat->sdp_session);
+		_LOGD (context, "SDP search failed: %s (%d)",
+		       nm_strerror_native (errsv), errsv);
+		error = g_error_new (NM_BT_ERROR,
+		                     NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Service Discovery failed with %s (%d)",
+		                     nm_strerror_native (errsv), errsv);
+		_context_invoke_callback_fail_and_free (context, error);
+		return G_SOURCE_REMOVE;
 	}
 
-	/* Search progressed successfully. */
-	return TRUE;
+	if (context->cdat->rfcomm_sdp_search_error) {
+		_LOGD (context, "SDP search failed to complete: %s", context->cdat->rfcomm_sdp_search_error->message);
+		_context_invoke_callback_fail_and_free (context, context->cdat->rfcomm_sdp_search_error);
+		return G_SOURCE_REMOVE;
+	}
+
+	nm_clear_pointer (&context->cdat->sdp_session, sdp_close);
+
+	_connect_socket_connect (context);
+
+	return G_SOURCE_REMOVE;
 }
 
 static gboolean
-sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+_connect_sdp_session_start_on_idle_cb (gpointer user_data)
 {
 	NMBluez5DunContext *context = user_data;
-	sdp_list_t *search, *attrs;
+	gs_free_error GError *error = NULL;
+
+	context->cdat->source_id = 0;
+
+	_LOGD (context, "retry starting sdp-session...");
+
+	if (!_connect_sdp_session_start (context, &error))
+		_context_invoke_callback_fail_and_free (context, error);
+
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_sdp_io_cb (GIOChannel *io_channel,
+                    GIOCondition condition,
+                    gpointer user_data)
+{
+	NMBluez5DunContext *context = user_data;
+	sdp_list_t *search;
+	sdp_list_t *attrs;
 	uuid_t svclass;
 	uint16_t attr;
-	int fd, fd_err = 0;
-	int err;
+	int fd;
+	int errsv;
+	int fd_err = 0;
+	int r;
 	socklen_t len = sizeof (fd_err);
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
+
+	context->cdat->source_id = 0;
 
-	context->sdp_watch_id = 0;
+	fd = g_io_channel_unix_get_fd (io_channel);
+
+	_LOGD (context, "sdp-session ready to connect with fd=%d", fd);
 
-	fd = g_io_channel_unix_get_fd (channel);
 	if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &fd_err, &len) < 0) {
-		err = errno;
-		nm_log_dbg (LOGD_BT, "(%s -> %s): getsockopt error=%d",
-		            context->src_str, context->dst_str, err);
-	} else {
-		err = fd_err;
-		nm_log_dbg (LOGD_BT, "(%s -> %s): SO_ERROR error=%d",
-		            context->src_str, context->dst_str, fd_err);
+		errsv = NM_ERRNO_NATIVE (errno);
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "error for getsockopt on Service Discovery socket: %s (%d)",
+		                     nm_strerror_native (errsv), errsv);
+		goto done;
 	}
 
-	if (err != 0) {
+	if (fd_err != 0) {
+		errsv = nm_errno_native (fd_err);
+
+		if (   NM_IN_SET (errsv, ECONNREFUSED, EHOSTDOWN)
+		    && --context->cdat->sdp_session_try_count > 0) {
+			/* *sigh* */
+			_LOGD (context, "sdp-session failed with %s (%d). Retry in a bit", nm_strerror_native (errsv), errsv);
+			nm_clear_g_source (&context->cdat->source_id);
+			context->cdat->source_id = g_timeout_add (1000,
+			                                          _connect_sdp_session_start_on_idle_cb,
+			                                          context);
+			return G_SOURCE_REMOVE;
+		}
+
 		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Error on Service Discovery socket: (%d) %s",
-		                     err, nm_strerror_native (err));
+		                     "error on Service Discovery socket: %s (%d)",
+		                     nm_strerror_native (errsv), errsv);
 		goto done;
 	}
 
-	if (sdp_set_notify (context->sdp_session, sdp_search_completed_cb, context) < 0) {
+	if (sdp_set_notify (context->cdat->sdp_session, _connect_sdp_search_cb, context) < 0) {
 		/* Should not be reached, only can fail if we passed bad sdp_session. */
 		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Could not request Service Discovery notification");
+		                     "could not set Service Discovery notification");
 		goto done;
 	}
 
@@ -286,124 +597,261 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da
 	attr = SDP_ATTR_PROTO_DESC_LIST;
 	attrs = sdp_list_append (NULL, &attr);
 
-	if (!sdp_service_search_attr_async (context->sdp_session, search, SDP_ATTR_REQ_INDIVIDUAL, attrs)) {
-		/* Set callback responsible for update the internal SDP transaction */
-		context->sdp_watch_id = g_io_add_watch (channel,
-		                                        G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-		                                        sdp_search_process_cb,
-		                                        context);
-	} else {
-		err = sdp_get_error (context->sdp_session);
-		error = g_error_new (NM_BT_ERROR,
-		                     NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Error starting Service Discovery: (%d) %s",
-		                     err, nm_strerror_native (err));
-	}
+	r = sdp_service_search_attr_async (context->cdat->sdp_session,
+	                                   search,
+	                                   SDP_ATTR_REQ_INDIVIDUAL,
+	                                   attrs);
 
 	sdp_list_free (attrs, NULL);
 	sdp_list_free (search, NULL);
 
-done:
-	if (error) {
-		context->callback (context, NULL, error, context->user_data);
-		sdp_search_cleanup (context);
+	if (r < 0) {
+		errsv = nm_errno_native (sdp_get_error (context->cdat->sdp_session));
+		error = g_error_new (NM_BT_ERROR,
+		                     NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "error starting Service Discovery: %s (%d)",
+		                     nm_strerror_native (errsv), errsv);
+		goto done;
 	}
 
+	/* Set callback responsible for update the internal SDP transaction */
+	context->cdat->source_id = g_io_add_watch (io_channel,
+	                                           G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+	                                           _connect_sdp_search_io_cb,
+	                                           context);
+
+done:
+	if (error)
+		_context_invoke_callback_fail_and_free (context, error);
 	return G_SOURCE_REMOVE;
 }
 
-NMBluez5DunContext *
-nm_bluez5_dun_new (const char *adapter,
-                   const char *remote)
+/*****************************************************************************/
 
+static void
+_connect_cancelled_cb (GCancellable *cancellable,
+                       NMBluez5DunContext *context)
 {
-	NMBluez5DunContext *context;
-
-	context = g_slice_new0 (NMBluez5DunContext);
-	str2ba (adapter, &context->src);
-	str2ba (remote, &context->dst);
-	context->src_str = g_strdup (adapter);
-	context->dst_str = g_strdup (remote);
-	context->rfcomm_channel = -1;
-	context->rfcomm_id = -1;
-	context->rfcomm_fd = -1;
-	return context;
+	gs_free_error GError *error = NULL;
+
+	if (!g_cancellable_set_error_if_cancelled (cancellable, &error))
+		g_return_if_reached ();
+
+	_context_invoke_callback_fail_and_free (context, error);
 }
 
-void
-nm_bluez5_dun_connect (NMBluez5DunContext *context,
-                       NMBluez5DunFunc callback,
-                       gpointer user_data)
+static gboolean
+_connect_sdp_session_start (NMBluez5DunContext *context,
+                            GError **error)
 {
-	GIOChannel *channel;
+	nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
 
-	context->callback = callback;
-	context->user_data = user_data;
+	nm_assert (context->cdat);
 
-	if (context->rfcomm_channel != -1) {
-		nm_log_dbg (LOGD_BT, "(%s): channel number on device %s cached: %d",
-		            context->src_str, context->dst_str, context->rfcomm_channel);
-		/* FIXME: don't invoke the callback synchronously. */
-		dun_connect (context);
-		return;
+	nm_clear_g_source (&context->cdat->source_id);
+	nm_clear_pointer (&context->cdat->sdp_session, sdp_close);
+
+	context->cdat->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
+	if (!context->cdat->sdp_session) {
+		int errsv = nm_errno_native (errno);
+
+		g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "failed to connect to the SDP server: %s (%d)",
+		             nm_strerror_native (errsv), errsv);
+		return FALSE;
 	}
 
-	nm_log_dbg (LOGD_BT, "(%s): starting channel number discovery for device %s",
-	            context->src_str, context->dst_str);
+	io_channel = g_io_channel_unix_new (sdp_get_socket (context->cdat->sdp_session));
+	context->cdat->source_id = g_io_add_watch (io_channel,
+	                                           G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+	                                           _connect_sdp_io_cb,
+	                                           context);
+	return TRUE;
+}
 
-	context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
-	if (!context->sdp_session) {
-		GError *error;
-		int err = sdp_get_error (context->sdp_session);
+/*****************************************************************************/
+
+gboolean
+nm_bluez5_dun_connect (const char *adapter,
+                       const char *remote,
+                       GCancellable *cancellable,
+                       NMBluez5DunConnectCb callback,
+                       gpointer callback_user_data,
+                       NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb,
+                       gpointer notify_tty_hangup_user_data,
+                       GError **error)
+{
+	nm_auto_free_context NMBluez5DunContext *context = NULL;
+	ConnectData *cdat;
+	gsize src_l;
+	gsize dst_l;
+
+	g_return_val_if_fail (adapter, FALSE);
+	g_return_val_if_fail (remote, FALSE);
+	g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
+	g_return_val_if_fail (callback, FALSE);
+	g_return_val_if_fail (notify_tty_hangup_cb, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+	nm_assert (!g_cancellable_is_cancelled (cancellable));
+
+	src_l = strlen (adapter) + 1;
+	dst_l = strlen (remote) + 1;
+
+	cdat = g_slice_new (ConnectData);
+	*cdat = (ConnectData) {
+		.callback              = callback,
+		.callback_user_data    = callback_user_data,
+		.cancellable           = g_object_ref (cancellable),
+		.sdp_session_try_count = 5,
+	};
 
-		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
-		                     "Failed to connect to the SDP server: (%d) %s",
-		                      err, nm_strerror_native (err));
-		/* FIXME: don't invoke the callback synchronously. */
-		context->callback (context, NULL, error, context->user_data);
-		return;
+	context = g_malloc (sizeof (NMBluez5DunContext) + src_l + dst_l);
+	*context = (NMBluez5DunContext) {
+		.cdat                        = cdat,
+		.notify_tty_hangup_cb        = notify_tty_hangup_cb,
+		.notify_tty_hangup_user_data = notify_tty_hangup_user_data,
+		.rfcomm_tty_no               = -1,
+		.rfcomm_sock_fd              = -1,
+		.rfcomm_tty_fd               = -1,
+		.rfcomm_channel              = -1,
+	};
+	memcpy (&context->src_str[0], adapter, src_l);
+	context->dst_str = &context->src_str[src_l];
+	memcpy ((char *) context->dst_str, remote, dst_l);
+
+	if (str2ba (adapter, &context->src) < 0) {
+		g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "invalid source");
+		return FALSE;
 	}
 
-	/* FIXME(shutdown): make connect cancellable. */
-	channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session));
-	context->sdp_watch_id = g_io_add_watch (channel,
-	                                        G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-	                                        sdp_connect_watch,
-	                                        context);
-	g_io_channel_unref (channel);
+	if (str2ba (remote, &context->dst) < 0) {
+		g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		             "invalid remote");
+		return FALSE;
+	}
+
+	context->cdat->cancelled_id = g_signal_connect (context->cdat->cancellable,
+	                                                "cancelled",
+	                                                G_CALLBACK (_connect_cancelled_cb),
+	                                                context);
+
+	if (!_connect_sdp_session_start (context, error))
+		return FALSE;
+
+	_LOGD (context, "starting channel number discovery for device %s",
+	       context->dst_str);
+
+	g_steal_pointer (&context);
+	return TRUE;
 }
 
-/* Only clean up connection-related stuff to allow reconnect */
+/*****************************************************************************/
+
 void
-nm_bluez5_dun_cleanup (NMBluez5DunContext *context)
+nm_bluez5_dun_disconnect (NMBluez5DunContext *context)
 {
-	g_return_if_fail (context != NULL);
+	nm_assert (context);
+	nm_assert (!context->cdat);
 
-	sdp_search_cleanup (context);
+	_LOGD (context, "disconnecting DUN connection");
 
-	if (context->rfcomm_fd >= 0) {
-		if (context->rfcomm_id >= 0) {
-			struct rfcomm_dev_req req = { 0 };
+	_context_free (context);
+}
 
-			req.dev_id = context->rfcomm_id;
-			(void) ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req);
-			context->rfcomm_id = -1;
-		}
-		nm_close (context->rfcomm_fd);
-		context->rfcomm_fd = -1;
-	}
+/*****************************************************************************/
 
-	nm_close (context->rfcomm_tty_fd);
-	context->rfcomm_tty_fd = -1;
+static void
+_context_cleanup_connect_data (NMBluez5DunContext *context)
+{
+	ConnectData *cdat;
+
+	cdat = g_steal_pointer (&context->cdat);
+	if (!cdat)
+		return;
+
+	nm_clear_g_signal_handler (cdat->cancellable, &cdat->cancelled_id);
+
+	nm_clear_g_source (&cdat->source_id);
+
+	nm_clear_pointer (&cdat->sdp_session, sdp_close);
+
+	g_clear_object (&cdat->cancellable);
+
+	g_clear_error (&cdat->rfcomm_sdp_search_error);
+
+	nm_g_slice_free (cdat);
 }
 
-void
-nm_bluez5_dun_free (NMBluez5DunContext *context)
+static void
+_context_invoke_callback (NMBluez5DunContext *context,
+                          GError *error)
+{
+	NMBluez5DunConnectCb callback;
+	gpointer callback_user_data;
+
+	nm_assert (context);
+	nm_assert (context->cdat);
+	nm_assert (context->cdat->callback);
+	nm_assert (error || context->rfcomm_tty_path);
+
+	if (!error)
+		_LOGD (context, "connected via \"%s\"", context->rfcomm_tty_path);
+	else if (nm_utils_error_is_cancelled (error, FALSE))
+		_LOGD (context, "cancelled");
+	else
+		_LOGD (context, "failed to connect: %s", error->message);
+
+	callback = context->cdat->callback;
+	callback_user_data = context->cdat->callback_user_data;
+
+	_context_cleanup_connect_data (context);
+
+	callback (error ? NULL : context,
+	          error ? NULL : context->rfcomm_tty_path,
+	          error,
+	          callback_user_data);
+}
+
+static void
+_context_invoke_callback_success (NMBluez5DunContext *context)
 {
-	g_return_if_fail (context != NULL);
+	nm_assert (context->rfcomm_tty_path);
+	_context_invoke_callback (context, NULL);
+}
+
+static void
+_context_invoke_callback_fail_and_free (NMBluez5DunContext *context,
+                                        GError *error)
+{
+	nm_assert (error);
+	_context_invoke_callback (context, error);
+	_context_free (context);
+}
+
+static void
+_context_free (NMBluez5DunContext *context)
+{
+	nm_assert (context);
+
+	_context_cleanup_connect_data (context);
+
+	nm_clear_g_source (&context->rfcomm_tty_poll_id);
+
+	if (context->rfcomm_sock_fd >= 0) {
+		if (context->rfcomm_tty_no >= 0) {
+			struct rfcomm_dev_req req;
+
+			memset (&req, 0, sizeof (struct rfcomm_dev_req));
+			req.dev_id = context->rfcomm_tty_no;
+			context->rfcomm_tty_no = -1;
+			(void) ioctl (context->rfcomm_sock_fd, RFCOMMRELEASEDEV, &req);
+		}
+		nm_close (nm_steal_fd (&context->rfcomm_sock_fd));
+	}
 
-	nm_bluez5_dun_cleanup (context);
-	g_clear_pointer (&context->src_str, g_free);
-	g_clear_pointer (&context->dst_str, g_free);
-	g_slice_free (NMBluez5DunContext, context);
+	if (context->rfcomm_tty_fd >= 0)
+		nm_close (nm_steal_fd (&context->rfcomm_tty_fd));
+	nm_clear_g_free (&context->rfcomm_tty_path);
+	g_free (context);
 }
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
index b605414b..b3f22965 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.h
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -1,43 +1,38 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
-#ifndef _NM_BLUEZ5_UTILS_H_
-#define _NM_BLUEZ5_UTILS_H_
+#ifndef __NM_BLUEZ5_DUN_H__
+#define __NM_BLUEZ5_DUN_H__
 
 typedef struct _NMBluez5DunContext NMBluez5DunContext;
 
-typedef void (*NMBluez5DunFunc) (NMBluez5DunContext *context,
-                                 const char *rfcomm_dev,
-                                 GError *error,
-                                 gpointer user_data);
+#if WITH_BLUEZ5_DUN
 
-NMBluez5DunContext *nm_bluez5_dun_new (const char *adapter,
-                                       const char *remote);
+typedef void (*NMBluez5DunConnectCb) (NMBluez5DunContext *context,
+                                      const char *rfcomm_dev,
+                                      GError *error,
+                                      gpointer user_data);
 
-void nm_bluez5_dun_connect (NMBluez5DunContext *context,
-                            NMBluez5DunFunc callback,
-                            gpointer user_data);
+typedef void (*NMBluez5DunNotifyTtyHangupCb) (NMBluez5DunContext *context,
+                                              gpointer user_data);
 
-/* Clean up connection resources */
-void nm_bluez5_dun_cleanup (NMBluez5DunContext *context);
+gboolean nm_bluez5_dun_connect (const char *adapter,
+                                const char *remote,
+                                GCancellable *cancellable,
+                                NMBluez5DunConnectCb callback,
+                                gpointer callback_user_data,
+                                NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb,
+                                gpointer notify_tty_hangup_user_data,
+                                GError **error);
 
-/* Clean up and dispose all resources */
-void nm_bluez5_dun_free (NMBluez5DunContext *context);
+void nm_bluez5_dun_disconnect (NMBluez5DunContext *context);
 
-#endif  /* _NM_BLUEZ5_UTILS_H_ */
+const char *nm_bluez5_dun_context_get_adapter (const NMBluez5DunContext *context);
+const char *nm_bluez5_dun_context_get_remote (const NMBluez5DunContext *context);
+const char *nm_bluez5_dun_context_get_rfcomm_dev (const NMBluez5DunContext *context);
+
+#endif /* WITH_BLUEZ5_DUN */
+
+#endif  /* __NM_BLUEZ5_DUN_H__ */
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
deleted file mode 100644
index 9c5e8644..00000000
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ /dev/null
@@ -1,596 +0,0 @@
-/* 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) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2017 Red Hat, Inc.
- * Copyright (C) 2013 Intel Corporation.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez5-manager.h"
-
-#include <signal.h>
-#include <stdlib.h>
-
-#include "nm-core-internal.h"
-
-#include "c-list/src/c-list.h"
-#include "nm-bluez-device.h"
-#include "nm-bluez-common.h"
-#include "devices/nm-device-bridge.h"
-#include "settings/nm-settings.h"
-
-/*****************************************************************************/
-
-enum {
-	BDADDR_ADDED,
-	NETWORK_SERVER_ADDED,
-	LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
-	NMSettings *settings;
-
-	GDBusProxy *proxy;
-
-	GHashTable *devices;
-
-	CList network_servers;
-} NMBluez5ManagerPrivate;
-
-struct _NMBluez5Manager {
-	GObject parent;
-	NMBtVTableNetworkServer network_server_vtable;
-	NMBluez5ManagerPrivate _priv;
-};
-
-struct _NMBluez5ManagerClass {
-	GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluez5Manager, nm_bluez5_manager, G_TYPE_OBJECT)
-
-#define NM_BLUEZ5_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluez5Manager, NM_IS_BLUEZ5_MANAGER)
-
-#define NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE(self) (&(self)->network_server_vtable)
-#define NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER(vtable) \
-	NM_BLUEZ5_MANAGER(((char *)(vtable)) - offsetof (struct _NMBluez5Manager, network_server_vtable))
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN LOGD_BT
-#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez5", __VA_ARGS__)
-
-/*****************************************************************************/
-
-static void device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self);
-static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self);
-
-/*****************************************************************************/
-
-typedef struct {
-	char *path;
-	char *addr;
-	NMDevice *device;
-	CList lst_ns;
-} NetworkServer;
-
-static NetworkServer *
-_find_network_server (NMBluez5Manager *self, const char *path, NMDevice *device)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NetworkServer *network_server;
-
-	nm_assert (path || NM_IS_DEVICE (device));
-
-	c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) {
-		if (path && !nm_streq (network_server->path, path))
-			continue;
-		if (device && network_server->device != device)
-			continue;
-		return network_server;
-	}
-	return NULL;
-}
-
-static NetworkServer *
-_find_network_server_for_addr (NMBluez5Manager *self, const char *addr)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NetworkServer *network_server;
-
-	c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) {
-		/* The address lookups need a server not assigned to a device
-		 * and tolerate an empty address as a wildcard for "any". */
-		if (   !network_server->device
-		    && (!addr || nm_streq (network_server->addr, addr)))
-			return network_server;
-	}
-	return NULL;
-}
-
-static void
-_network_server_unregister (NMBluez5Manager *self, NetworkServer *network_server)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
-	if (!network_server->device) {
-		/* Not connected. */
-		return;
-	}
-
-	_LOGI ("NAP: unregistering %s from %s",
-	       nm_device_get_iface (network_server->device),
-	       network_server->addr);
-
-	g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
-	                        NM_BLUEZ_SERVICE,
-	                        network_server->path,
-	                        NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
-	                        "Unregister",
-	                        g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
-	                        NULL,
-	                        G_DBUS_CALL_FLAGS_NONE,
-	                        -1, NULL, NULL, NULL);
-
-	g_clear_object (&network_server->device);
-}
-
-static void
-_network_server_free (NMBluez5Manager *self, NetworkServer *network_server)
-{
-	_network_server_unregister (self, network_server);
-	c_list_unlink_stale (&network_server->lst_ns);
-	g_free (network_server->path);
-	g_free (network_server->addr);
-	g_slice_free (NetworkServer, network_server);
-}
-
-static gboolean
-network_server_is_available (const NMBtVTableNetworkServer *vtable,
-                             const char *addr)
-{
-	NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
-
-	return !!_find_network_server_for_addr (self, addr);
-}
-
-static gboolean
-network_server_register_bridge (const NMBtVTableNetworkServer *vtable,
-                                const char *addr,
-                                NMDevice *device)
-{
-	NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NetworkServer *network_server = _find_network_server_for_addr (self, addr);
-
-	nm_assert (NM_IS_DEVICE (device));
-	nm_assert (!_find_network_server (self, NULL, device));
-
-	if (!network_server) {
-		/* The device checked that a network server is available, before
-		 * starting the activation, but for some reason it no longer is.
-		 * Indicate that the activation should not proceed. */
-		_LOGI ("NAP: %s is not available for %s", addr, nm_device_get_iface (device));
-		return FALSE;
-	}
-
-	_LOGI ("NAP: registering %s on %s", nm_device_get_iface (device), network_server->addr);
-
-	g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
-	                        NM_BLUEZ_SERVICE,
-	                        network_server->path,
-	                        NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
-	                        "Register",
-	                        g_variant_new ("(ss)", BLUETOOTH_CONNECT_NAP, nm_device_get_iface (device)),
-	                        NULL,
-	                        G_DBUS_CALL_FLAGS_NONE,
-	                        -1, NULL, NULL, NULL);
-
-	network_server->device = g_object_ref (device);
-
-	return TRUE;
-}
-
-static gboolean
-network_server_unregister_bridge (const NMBtVTableNetworkServer *vtable,
-                                  NMDevice *device)
-{
-	NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
-	NetworkServer *network_server = _find_network_server (self, NULL, device);
-
-	if (network_server)
-		_network_server_unregister (self, network_server);
-
-	return TRUE;
-}
-
-static void
-network_server_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
-	NetworkServer *network_server;
-
-	network_server = _find_network_server (self, path, NULL);
-	if (!network_server)
-		return;
-
-	if (network_server->device) {
-		nm_device_queue_state (network_server->device, NM_DEVICE_STATE_DISCONNECTED,
-		                       NM_DEVICE_STATE_REASON_BT_FAILED);
-	}
-	_LOGI ("NAP: removed interface %s", network_server->addr);
-	_network_server_free (self, network_server);
-}
-
-static void
-network_server_added (GDBusProxy *proxy, const char *path, const char *addr, NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NetworkServer *network_server;
-
-	/* If BlueZ messes up and announces a single network server twice,
-	 * make sure we get rid of the older instance first. */
-	network_server_removed (proxy, path, self);
-
-	network_server = g_slice_new0 (NetworkServer);
-	network_server->path = g_strdup (path);
-	network_server->addr = g_strdup (addr);
-	c_list_link_before (&priv->network_servers, &network_server->lst_ns);
-
-	_LOGI ("NAP: added interface %s", addr);
-
-	g_signal_emit (self, signals[NETWORK_SERVER_ADDED], 0);
-}
-
-/*****************************************************************************/
-
-static void
-emit_bdaddr_added (NMBluez5Manager *self, NMBluezDevice *device)
-{
-	g_signal_emit (self, signals[BDADDR_ADDED], 0,
-	               device,
-	               nm_bluez_device_get_address (device),
-	               nm_bluez_device_get_name (device),
-	               nm_bluez_device_get_path (device),
-	               nm_bluez_device_get_capabilities (device));
-}
-
-void
-nm_bluez5_manager_query_devices (NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NMBluezDevice *device;
-	GHashTableIter iter;
-
-	g_hash_table_iter_init (&iter, priv->devices);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
-		if (nm_bluez_device_get_usable (device))
-			emit_bdaddr_added (self, device);
-	}
-}
-
-static void
-remove_device (NMBluez5Manager *self, NMBluezDevice *device)
-{
-	g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_initialized), self);
-	g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_usable), self);
-	if (nm_bluez_device_get_usable (device))
-		g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
-}
-
-static void
-remove_all_devices (NMBluez5Manager *self)
-{
-	GHashTableIter iter;
-	NMBluezDevice *device;
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
-	g_hash_table_iter_init (&iter, priv->devices);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
-		g_hash_table_iter_steal (&iter);
-		remove_device (self, device);
-		g_object_unref (device);
-	}
-}
-
-static void
-device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self)
-{
-	gboolean usable = nm_bluez_device_get_usable (device);
-
-	_LOGD ("(%s): bluez device now %s",
-	       nm_bluez_device_get_path (device),
-	       usable ? "usable" : "unusable");
-
-	if (usable) {
-		_LOGD ("(%s): bluez device address %s",
-		       nm_bluez_device_get_path (device),
-		       nm_bluez_device_get_address (device));
-		emit_bdaddr_added (self, device);
-	} else
-		g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
-}
-
-static void
-device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
-	_LOGD ("(%s): bluez device %s",
-	       nm_bluez_device_get_path (device),
-	       success ? "initialized" : "failed to initialize");
-	if (!success)
-		g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device));
-}
-
-static void
-device_added (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NMBluezDevice *device;
-
-	device = nm_bluez_device_new (path, NULL, priv->settings, 5);
-	g_signal_connect (device, NM_BLUEZ_DEVICE_INITIALIZED, G_CALLBACK (device_initialized), self);
-	g_signal_connect (device, "notify::" NM_BLUEZ_DEVICE_USABLE, G_CALLBACK (device_usable), self);
-	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
-
-	_LOGD ("(%s): new bluez device found", path);
-}
-
-static void
-device_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NMBluezDevice *device;
-
-	_LOGD ("(%s): bluez device removed", path);
-
-	device = g_hash_table_lookup (priv->devices, path);
-	if (device) {
-		g_hash_table_steal (priv->devices, nm_bluez_device_get_path (device));
-		remove_device (NM_BLUEZ5_MANAGER (self), device);
-		g_object_unref (device);
-	}
-}
-
-static void
-object_manager_interfaces_added (GDBusProxy      *proxy,
-                                 const char      *path,
-                                 GVariant        *dict,
-                                 NMBluez5Manager *self)
-{
-	if (g_variant_lookup (dict, NM_BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL))
-		device_added (proxy, path, self);
-	if (g_variant_lookup (dict, NM_BLUEZ5_NETWORK_SERVER_INTERFACE, "a{sv}", NULL)) {
-		gs_unref_variant GVariant *adapter = g_variant_lookup_value (dict, NM_BLUEZ5_ADAPTER_INTERFACE, G_VARIANT_TYPE_DICTIONARY);
-		const char *address;
-
-		if (   adapter
-		    && g_variant_lookup (adapter, "Address", "&s", &address))
-			network_server_added (proxy, path, address, self);
-	}
-}
-
-static void
-object_manager_interfaces_removed (GDBusProxy       *proxy,
-                                   const char       *path,
-                                   const char      **ifaces,
-                                   NMBluez5Manager  *self)
-{
-	if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_DEVICE_INTERFACE))
-		device_removed (proxy, path, self);
-	if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_NETWORK_SERVER_INTERFACE))
-		network_server_removed (proxy, path, self);
-}
-
-static void
-get_managed_objects_cb (GDBusProxy *proxy,
-                        GAsyncResult *res,
-                        NMBluez5Manager *self)
-{
-	gs_unref_variant GVariant *variant0 = NULL;
-	GVariant *variant, *ifaces;
-	GVariantIter i;
-	GError *error = NULL;
-	const char *path;
-
-	variant = _nm_dbus_proxy_call_finish (proxy, res,
-	                                      G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
-	                                      &error);
-	if (!variant) {
-		if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
-			_LOGW ("Couldn't get managed objects: not running Bluez5?");
-		else {
-			g_dbus_error_strip_remote_error (error);
-			_LOGW ("Couldn't get managed objects: %s", error->message);
-		}
-		g_clear_error (&error);
-		return;
-	}
-	variant0 = g_variant_get_child_value (variant, 0);
-	g_variant_iter_init (&i, variant0);
-	while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) {
-		object_manager_interfaces_added (proxy, path, ifaces, self);
-		g_variant_unref (ifaces);
-	}
-
-	g_variant_unref (variant);
-}
-
-static void name_owner_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data);
-
-static void
-on_proxy_acquired (GObject *object,
-                   GAsyncResult *res,
-                   NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	GError *error = NULL;
-
-	priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
-	if (!priv->proxy) {
-		_LOGW ("Couldn't acquire object manager proxy: %s", error->message);
-		g_clear_error (&error);
-		return;
-	}
-
-	g_signal_connect (priv->proxy, "notify::g-name-owner",
-	                  G_CALLBACK (name_owner_changed_cb), self);
-
-	/* Get already managed devices. */
-	g_dbus_proxy_call (priv->proxy, "GetManagedObjects",
-	                   NULL,
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   NULL,
-	                   (GAsyncReadyCallback) get_managed_objects_cb,
-	                   self);
-
-	_nm_dbus_signal_connect (priv->proxy, "InterfacesAdded", G_VARIANT_TYPE ("(oa{sa{sv}})"),
-	                         G_CALLBACK (object_manager_interfaces_added), self);
-	_nm_dbus_signal_connect (priv->proxy, "InterfacesRemoved", G_VARIANT_TYPE ("(oas)"),
-	                         G_CALLBACK (object_manager_interfaces_removed), self);
-}
-
-static void
-bluez_connect (NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
-	g_return_if_fail (priv->proxy == NULL);
-
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_NONE,
-	                          NULL,
-	                          NM_BLUEZ_SERVICE,
-	                          NM_BLUEZ_MANAGER_PATH,
-	                          NM_OBJECT_MANAGER_INTERFACE,
-	                          NULL,
-	                          (GAsyncReadyCallback) on_proxy_acquired,
-	                          self);
-}
-
-static void
-name_owner_changed_cb (GObject *object,
-                       GParamSpec *pspec,
-                       gpointer user_data)
-{
-	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (user_data);
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	char *owner;
-
-	if (priv->devices) {
-		owner = g_dbus_proxy_get_name_owner (priv->proxy);
-		if (!owner)
-			remove_all_devices (self);
-		g_free (owner);
-	}
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez5_manager_init (NMBluez5Manager *self)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	NMBtVTableNetworkServer *network_server_vtable = NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE (self);
-
-	bluez_connect (self);
-
-	priv->devices = g_hash_table_new_full (nm_str_hash, g_str_equal,
-	                                       NULL, g_object_unref);
-
-	c_list_init (&priv->network_servers);
-
-	nm_assert (!nm_bt_vtable_network_server);
-	network_server_vtable->is_available = network_server_is_available;
-	network_server_vtable->register_bridge = network_server_register_bridge;
-	network_server_vtable->unregister_bridge = network_server_unregister_bridge;
-	nm_bt_vtable_network_server = network_server_vtable;
-}
-
-NMBluez5Manager *
-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)->settings = g_object_ref (settings);
-	return instance;
-}
-
-static void
-dispose (GObject *object)
-{
-	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	CList *iter, *safe;
-
-	c_list_for_each_safe (iter, safe, &priv->network_servers)
-		_network_server_free (self, c_list_entry (iter, NetworkServer, lst_ns));
-
-	if (priv->proxy) {
-		g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self);
-		g_clear_object (&priv->proxy);
-	}
-
-	g_hash_table_remove_all (priv->devices);
-
-	G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->dispose (object);
-}
-
-static void
-finalize (GObject *object)
-{
-	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
-	g_hash_table_destroy (priv->devices);
-
-	G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->finalize (object);
-
-	g_object_unref (priv->settings);
-}
-
-static void
-nm_bluez5_manager_class_init (NMBluez5ManagerClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	object_class->dispose = dispose;
-	object_class->finalize = finalize;
-
-	signals[BDADDR_ADDED] =
-	    g_signal_new (NM_BLUEZ_MANAGER_BDADDR_ADDED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
-	                  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
-
-	signals[NETWORK_SERVER_ADDED] =
-	    g_signal_new (NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 0);
-}
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
deleted file mode 100644
index 14ac842e..00000000
--- a/src/devices/bluetooth/nm-bluez5-manager.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* 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) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2013 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ5_MANAGER_H__
-#define __NETWORKMANAGER_BLUEZ5_MANAGER_H__
-
-#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))
-#define NM_IS_BLUEZ5_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ5_MANAGER))
-#define NM_IS_BLUEZ5_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ5_MANAGER))
-#define NM_BLUEZ5_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
-
-typedef struct _NMBluez5Manager NMBluez5Manager;
-typedef struct _NMBluez5ManagerClass NMBluez5ManagerClass;
-
-GType nm_bluez5_manager_get_type (void);
-
-NMBluez5Manager *nm_bluez5_manager_new (NMSettings *settings);
-
-void nm_bluez5_manager_query_devices (NMBluez5Manager *manager);
-
-#endif /* __NETWORKMANAGER_BLUEZ5_MANAGER_H__ */
diff --git a/src/devices/bluetooth/nm-bt-error.c b/src/devices/bluetooth/nm-bt-error.c
index bc9e5aa4..8aa4283d 100644
--- a/src/devices/bluetooth/nm-bt-error.c
+++ b/src/devices/bluetooth/nm-bt-error.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
diff --git a/src/devices/bluetooth/nm-bt-error.h b/src/devices/bluetooth/nm-bt-error.h
index ed7ed90d..258fca8d 100644
--- a/src/devices/bluetooth/nm-bt-error.h
+++ b/src/devices/bluetooth/nm-bt-error.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index f8626011..497810c3 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2011 Red Hat, Inc.
  */
 
@@ -23,8 +9,9 @@
 
 #include <stdio.h>
 
+#include "nm-core-internal.h"
 #include "nm-bluez-common.h"
-#include "nm-bluez-device.h"
+#include "nm-bluez-manager.h"
 #include "devices/nm-device-private.h"
 #include "ppp/nm-ppp-manager.h"
 #include "nm-setting-connection.h"
@@ -48,10 +35,12 @@ _LOG_DECLARE_SELF(NMDeviceBt);
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
-	PROP_BT_NAME,
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceBt,
+	PROP_BT_BDADDR,
+	PROP_BT_BZ_MGR,
 	PROP_BT_CAPABILITIES,
-	PROP_BT_DEVICE,
+	PROP_BT_DBUS_PATH,
+	PROP_BT_NAME,
 );
 
 enum {
@@ -64,24 +53,38 @@ static guint signals[LAST_SIGNAL] = { 0 };
 typedef struct {
 	NMModemManager *modem_manager;
 
-	gboolean mm_running;
+	NMBluezManager *bz_mgr;
 
-	NMBluezDevice *bt_device;
+	char *dbus_path;
 
 	char *bdaddr;
 	char *name;
-	guint32 capabilities;
 
-	gboolean connected;
-	gboolean have_iface;
+	char *connect_rfcomm_iface;
+
+	GSList *connect_modem_candidates;
 
-	char *rfcomm_iface;
 	NMModem *modem;
-	guint timeout_id;
 
-	GCancellable *cancellable;
+	GCancellable *connect_bz_cancellable;
+
+	gulong connect_watch_link_id;
+
+	guint connect_watch_link_idle_id;
+
+	guint connect_wait_modem_id;
+
+	NMBluetoothCapabilities capabilities:6;
+
+	NMBluetoothCapabilities connect_bt_type:6;  /* BT type of the current connection */
+
+	NMDeviceStageState stage1_bt_state:3;
+	NMDeviceStageState stage1_modem_prepare_state:3;
+
+	bool is_connected:1;
+
+	bool mm_running:1;
 
-	guint32 bt_type;  /* BT type of the current connection */
 } NMDeviceBtPrivate;
 
 struct _NMDeviceBt {
@@ -95,42 +98,65 @@ struct _NMDeviceBtClass {
 
 G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
 
-#define NM_DEVICE_BT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceBt, NM_IS_DEVICE_BT)
-
-/*****************************************************************************/
-
-static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason);
+#define NM_DEVICE_BT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceBt, NM_IS_DEVICE_BT, NMDevice)
 
 /*****************************************************************************/
 
-guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
+NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE);
 
 	return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities;
 }
 
-static guint32
+static NMBluetoothCapabilities
 get_connection_bt_type (NMConnection *connection)
 {
 	NMSettingBluetooth *s_bt;
 	const char *bt_type;
 
 	s_bt = nm_connection_get_setting_bluetooth (connection);
-	if (!s_bt)
-		return NM_BT_CAPABILITY_NONE;
 
-	bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
-	g_assert (bt_type);
-
-	if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
-		return NM_BT_CAPABILITY_DUN;
-	else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
-		return NM_BT_CAPABILITY_NAP;
+	if (s_bt) {
+		bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+		if (bt_type) {
+			if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+				return NM_BT_CAPABILITY_DUN;
+			else if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+				return NM_BT_CAPABILITY_NAP;
+		}
+	}
 
 	return NM_BT_CAPABILITY_NONE;
 }
 
+static gboolean
+get_connection_bt_type_check (NMDeviceBt *self,
+                              NMConnection *connection,
+                              NMBluetoothCapabilities *out_bt_type,
+                              GError **error)
+{
+	NMBluetoothCapabilities bt_type;
+
+	bt_type = get_connection_bt_type (connection);
+
+	NM_SET_OUT (out_bt_type, bt_type);
+
+	if (bt_type == NM_BT_CAPABILITY_NONE) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "profile is not a PANU/DUN bluetooth type");
+		return FALSE;
+	}
+
+	if (!NM_FLAGS_ALL (NM_DEVICE_BT_GET_PRIVATE (self)->capabilities, bt_type)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "device does not support bluetooth type");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *device)
 {
@@ -142,17 +168,24 @@ can_auto_connect (NMDevice *device,
                   NMSettingsConnection *sett_conn,
                   char **specific_object)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
-	guint32 bt_type;
+	NMDeviceBt *self = NM_DEVICE_BT (device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	NMBluetoothCapabilities bt_type;
 
 	nm_assert (!specific_object || !*specific_object);
 
 	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->can_auto_connect (device, sett_conn, NULL))
 		return FALSE;
 
+	if (!get_connection_bt_type_check (self,
+	                                   nm_settings_connection_get_connection (sett_conn),
+	                                   &bt_type,
+	                                   NULL))
+		return FALSE;
+
 	/* Can't auto-activate a DUN connection without ModemManager */
-	bt_type = get_connection_bt_type (nm_settings_connection_get_connection (sett_conn));
-	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+	if (   bt_type == NM_BT_CAPABILITY_DUN
+	    && priv->mm_running == FALSE)
 		return FALSE;
 
 	return TRUE;
@@ -161,20 +194,16 @@ can_auto_connect (NMDevice *device,
 static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+	NMDeviceBt *self = NM_DEVICE_BT (device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 	NMSettingBluetooth *s_bt;
 	const char *bdaddr;
-	guint32 bt_type;
 
 	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
-	bt_type = get_connection_bt_type (connection);
-	if (!NM_FLAGS_ALL (priv->capabilities, bt_type)) {
-		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
-		                            "device does not support bluetooth type of profile");
+	if (!get_connection_bt_type_check (self, connection, NULL, error))
 		return FALSE;
-	}
 
 	s_bt = nm_connection_get_setting_bluetooth (connection);
 
@@ -200,18 +229,15 @@ check_connection_available (NMDevice *device,
                             const char *specific_object,
                             GError **error)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
-	guint32 bt_type;
+	NMDeviceBt *self = NM_DEVICE_BT (device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	NMBluetoothCapabilities bt_type;
 
-	bt_type = get_connection_bt_type (connection);
-	if (!(bt_type & priv->capabilities)) {
-		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
-		                            "device does not support bluetooth type");
+	if (!get_connection_bt_type_check (self, connection, &bt_type, error))
 		return FALSE;
-	}
 
-	/* DUN connections aren't available without ModemManager */
-	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE) {
+	if (   bt_type == NM_BT_CAPABILITY_DUN
+	    && !priv->mm_running) {
 		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
 		                            "ModemManager missing for DUN profile");
 		return FALSE;
@@ -227,11 +253,12 @@ complete_connection (NMDevice *device,
                      NMConnection *const*existing_connections,
                      GError **error)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMSettingBluetooth *s_bt;
 	const char *setting_bdaddr;
 	const char *ctype;
-	gboolean is_dun = FALSE, is_pan = FALSE;
+	gboolean is_dun = FALSE;
+	gboolean is_pan = FALSE;
 	NMSettingGsm *s_gsm;
 	NMSettingCdma *s_cdma;
 	NMSettingSerial *s_serial;
@@ -274,7 +301,10 @@ complete_connection (NMDevice *device,
 		}
 
 		/* PAN can't use any DUN-related settings */
-		if (s_gsm || s_cdma || s_serial || s_ppp) {
+		if (   s_gsm
+		    || s_cdma
+		    || s_serial
+		    || s_ppp) {
 			g_set_error_literal (error,
 			                     NM_CONNECTION_ERROR,
 			                     NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -304,7 +334,8 @@ complete_connection (NMDevice *device,
 		}
 
 		/* Need at least a GSM or a CDMA setting */
-		if (!s_gsm && !s_cdma) {
+		if (   !s_gsm
+		    && !s_cdma) {
 			g_set_error_literal (error,
 			                     NM_CONNECTION_ERROR,
 			                     NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -441,20 +472,19 @@ static void
 modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+
+	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
 
 	if (error) {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-	} else {
-		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
-
-		/* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */
-		g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
-		if (!modem_stage1 (NM_DEVICE_BT (device), priv->modem, &failure_reason))
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, failure_reason);
+		return;
 	}
+
+	priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_INIT;
+	nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 static void
@@ -463,45 +493,33 @@ modem_prepare_result (NMModem *modem,
                       guint i_reason,
                       gpointer user_data)
 {
-	NMDeviceBt *self = NM_DEVICE_BT (user_data);
-	NMDevice *device = NM_DEVICE (self);
+	NMDeviceBt *self = user_data;
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 	NMDeviceStateReason reason = i_reason;
 	NMDeviceState state;
 
-	state = nm_device_get_state (device);
-	g_return_if_fail (state == NM_DEVICE_STATE_CONFIG || state == NM_DEVICE_STATE_NEED_AUTH);
-
-	if (success) {
-		NMActRequest *req;
-		NMActStageReturn ret;
-		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
-
-		req = nm_device_get_act_request (device);
-		g_return_if_fail (req);
-
-		ret = nm_modem_act_stage2_config (modem, req, &failure_reason);
-		switch (ret) {
-		case NM_ACT_STAGE_RETURN_POSTPONE:
-			break;
-		case NM_ACT_STAGE_RETURN_SUCCESS:
-			nm_device_activate_schedule_stage3_ip_config_start (device);
-			break;
-		case NM_ACT_STAGE_RETURN_FAILURE:
-		default:
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, failure_reason);
-			break;
-		}
-	} else {
+	state = nm_device_get_state (NM_DEVICE (self));
+
+	g_return_if_fail (NM_IN_SET (state, NM_DEVICE_STATE_PREPARE,
+	                                    NM_DEVICE_STATE_NEED_AUTH));
+
+	nm_assert (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_PENDING);
+
+	if (!success) {
 		if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
 			/* If the connect failed because the SIM PIN was wrong don't allow
 			 * the device to be auto-activated anymore, which would risk locking
 			 * the SIM if the incorrect PIN continues to be used.
 			 */
-			nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN);
+			nm_device_autoconnect_blocked_set (NM_DEVICE (self), NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN);
 		}
 
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
+		return;
 	}
+
+	priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+	nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
 }
 
 static void
@@ -510,7 +528,7 @@ device_state_changed (NMDevice *device,
                       NMDeviceState old_state,
                       NMDeviceStateReason reason)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 
 	if (priv->modem)
 		nm_modem_device_state_changed (priv->modem, new_state, old_state);
@@ -519,7 +537,8 @@ device_state_changed (NMDevice *device,
 	 * since the device could be both DUN and NAP capable and thus may not
 	 * change state (which rechecks available connections) when MM comes and goes.
 	 */
-	if (priv->mm_running && (priv->capabilities & NM_BT_CAPABILITY_DUN))
+	if (   priv->mm_running
+	    && NM_FLAGS_HAS (priv->capabilities, NM_BT_CAPABILITY_DUN))
 	    nm_device_recheck_available_connections (device);
 }
 
@@ -541,8 +560,10 @@ modem_ip4_config_result (NMModem *modem,
 		nm_device_ip_method_failed (device,
 		                            AF_INET,
 		                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-	} else
-		nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config));
+		return;
+	}
+
+	nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config));
 }
 
 static void
@@ -561,29 +582,6 @@ ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 	}
 }
 
-static gboolean
-modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason)
-{
-	NMActRequest *req;
-	NMActStageReturn ret;
-
-	req = nm_device_get_act_request (NM_DEVICE (self));
-	g_return_val_if_fail (req, FALSE);
-
-	ret = nm_modem_act_stage1_prepare (modem, req, out_failure_reason);
-	switch (ret) {
-	case NM_ACT_STAGE_RETURN_POSTPONE:
-	case NM_ACT_STAGE_RETURN_SUCCESS:
-		/* Success, wait for the 'prepare-result' signal */
-		return TRUE;
-	case NM_ACT_STAGE_RETURN_FAILURE:
-	default:
-		break;
-	}
-
-	return FALSE;
-}
-
 /*****************************************************************************/
 
 static void
@@ -593,7 +591,7 @@ modem_cleanup (NMDeviceBt *self)
 
 	if (priv->modem) {
 		g_signal_handlers_disconnect_matched (priv->modem, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
-		g_clear_object (&priv->modem);
+		nm_clear_pointer (&priv->modem, nm_modem_unclaim);
 	}
 }
 
@@ -608,11 +606,13 @@ modem_state_cb (NMModem *modem,
 	NMDevice *device = NM_DEVICE (user_data);
 	NMDeviceState dev_state = nm_device_get_state (device);
 
-	if (new_state <= NM_MODEM_STATE_DISABLING && old_state > NM_MODEM_STATE_DISABLING) {
+	if (   new_state <= NM_MODEM_STATE_DISABLING
+	    && old_state > NM_MODEM_STATE_DISABLING) {
 		/* Will be called whenever something external to NM disables the
 		 * modem directly through ModemManager.
 		 */
-		if (nm_device_is_activating (device) || dev_state == NM_DEVICE_STATE_ACTIVATED) {
+		if (   nm_device_is_activating (device)
+		    || dev_state == NM_DEVICE_STATE_ACTIVATED) {
 			nm_device_state_changed (device,
 			                         NM_DEVICE_STATE_DISCONNECTED,
 			                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
@@ -620,13 +620,15 @@ modem_state_cb (NMModem *modem,
 		}
 	}
 
-	if (new_state < NM_MODEM_STATE_CONNECTING &&
-	    old_state >= NM_MODEM_STATE_CONNECTING &&
-	    dev_state >= NM_DEVICE_STATE_NEED_AUTH &&
-	    dev_state <= NM_DEVICE_STATE_ACTIVATED) {
+	if (   new_state < NM_MODEM_STATE_CONNECTING
+	    && old_state >= NM_MODEM_STATE_CONNECTING
+	    && dev_state >= NM_DEVICE_STATE_NEED_AUTH
+	    && dev_state <= NM_DEVICE_STATE_ACTIVATED) {
 		/* Fail the device if the modem disconnects unexpectedly while the
 		 * device is activating/activated. */
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
 		return;
 	}
 }
@@ -637,66 +639,56 @@ modem_removed_cb (NMModem *modem, gpointer user_data)
 	NMDeviceBt *self = NM_DEVICE_BT (user_data);
 	NMDeviceState state;
 
-	/* Fail the device if the modem was removed while active */
 	state = nm_device_get_state (NM_DEVICE (self));
-	if (   state == NM_DEVICE_STATE_ACTIVATED
-	    || nm_device_is_activating (NM_DEVICE (self))) {
+	if (   nm_device_is_activating (NM_DEVICE (self))
+	    || state == NM_DEVICE_STATE_ACTIVATED) {
 		nm_device_state_changed (NM_DEVICE (self),
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_BT_FAILED);
-	} else
-		modem_cleanup (self);
+		return;
+	}
+
+	modem_cleanup (self);
 }
 
 static gboolean
-component_added (NMDevice *device, GObject *component)
+modem_try_claim (NMDeviceBt *self,
+                 NMModem *modem)
 {
-	NMDeviceBt *self = NM_DEVICE_BT (device);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	NMModem *modem;
+	gs_free char *rfcomm_base_name = NULL;
 	NMDeviceState state;
-	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
-	if (   !component
-	    || !NM_IS_MODEM (component))
+	if (priv->modem) {
+		if (priv->modem == modem)
+			return TRUE;
 		return FALSE;
+	}
 
-	modem = NM_MODEM (component);
-	if (!priv->rfcomm_iface)
+	if (nm_modem_is_claimed (modem))
 		return FALSE;
 
-	{
-		gs_free char *base = NULL;
-
-		base = g_path_get_basename (priv->rfcomm_iface);
-		if (!nm_streq (base, nm_modem_get_control_port (modem)))
-			return FALSE;
-	}
+	if (!priv->connect_rfcomm_iface)
+		return FALSE;
 
-	/* Got the modem */
-	nm_clear_g_source (&priv->timeout_id);
-	nm_clear_g_cancellable (&priv->cancellable);
+	rfcomm_base_name = g_path_get_basename (priv->connect_rfcomm_iface);
+	if (!nm_streq0 (rfcomm_base_name, nm_modem_get_control_port (modem)))
+		return FALSE;
 
-	/* Can only accept the modem in stage2, but since the interface matched
+	/* Can only accept the modem in stage1, but since the interface matched
 	 * what we were expecting, don't let anything else claim the modem either.
 	 */
 	state = nm_device_get_state (NM_DEVICE (self));
-	if (state != NM_DEVICE_STATE_CONFIG) {
-		_LOGW (LOGD_BT | LOGD_MB,
+	if (state != NM_DEVICE_STATE_PREPARE) {
+		_LOGD (LOGD_BT | LOGD_MB,
 		       "modem found but device not in correct state (%d)",
 		       nm_device_get_state (NM_DEVICE (self)));
-		return TRUE;
+		return FALSE;
 	}
 
-	_LOGI (LOGD_BT | LOGD_MB,
-	       "Activation: (bluetooth) Stage 2 of 5 (Device Configure) modem found.");
+	priv->modem = nm_modem_claim (modem);
+	priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_INIT;
 
-	if (priv->modem) {
-		g_warn_if_reached ();
-		modem_cleanup (self);
-	}
-
-	priv->modem = g_object_ref (modem);
 	g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self);
 	g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
 	g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
@@ -705,92 +697,187 @@ component_added (NMDevice *device, GObject *component)
 	g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
 	g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
 	g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
-
 	g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self);
 
-	/* Kick off the modem connection */
-	if (!modem_stage1 (self, modem, &failure_reason))
-		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, failure_reason);
+	_LOGD (LOGD_BT | LOGD_MB,
+	       "modem found");
 
 	return TRUE;
 }
 
-static gboolean
-modem_find_timeout (gpointer user_data)
+static void
+mm_modem_added_cb (NMModemManager *manager,
+                   NMModem *modem,
+                   gpointer user_data)
+{
+	NMDeviceBt *self = user_data;
+	NMDeviceBtPrivate *priv;
+
+	if (!modem_try_claim (user_data, modem))
+		return;
+
+	priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_COMPLETED)
+		nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
+}
+
+/*****************************************************************************/
+
+void
+_nm_device_bt_notify_set_connected (NMDeviceBt *self,
+                                    gboolean connected)
 {
-	NMDeviceBt *self = NM_DEVICE_BT (user_data);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 
-	priv->timeout_id = 0;
-	nm_clear_g_cancellable (&priv->cancellable);
+	connected = !!connected;
+	if (priv->is_connected == connected)
+		return;
+
+	priv->is_connected = connected;
 
+	if (   connected
+	    || priv->stage1_bt_state != NM_DEVICE_STAGE_STATE_COMPLETED
+	    || nm_device_get_state (NM_DEVICE (self)) > NM_DEVICE_STATE_ACTIVATED) {
+		_LOGT (LOGD_BT, "set-connected: %d", connected);
+		return;
+	}
+
+	_LOGT (LOGD_BT, "set-connected: %d (disconnecting device...)", connected);
 	nm_device_state_changed (NM_DEVICE (self),
 	                         NM_DEVICE_STATE_FAILED,
-	                         NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
-	return FALSE;
+	                         NM_DEVICE_STATE_REASON_CARRIER);
 }
 
-static void
-check_connect_continue (NMDeviceBt *self)
+static gboolean
+connect_watch_link_idle_cb (gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (self);
+	NMDeviceBt *self = user_data;
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	gboolean pan = (priv->bt_type == NM_BT_CAPABILITY_NAP);
-	gboolean dun = (priv->bt_type == NM_BT_CAPABILITY_DUN);
+	int ifindex;
 
-	if (!priv->connected || !priv->have_iface)
-		return;
+	priv->connect_watch_link_idle_id = 0;
+
+	if (nm_device_get_state (NM_DEVICE (self)) <= NM_DEVICE_STATE_ACTIVATED) {
+		ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
+		if (   ifindex > 0
+		    && !nm_platform_link_get (nm_device_get_platform (NM_DEVICE (self)), ifindex)) {
+			_LOGT (LOGD_BT, "device disappeared");
+			nm_device_state_changed (NM_DEVICE (self),
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_BT_FAILED);
+		}
+	}
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+connect_watch_link_cb (NMPlatform *platform,
+                       int obj_type_i,
+                       int ifindex,
+                       NMPlatformLink *info,
+                       int change_type_i,
+                       NMDevice *self)
+{
+	const NMPlatformSignalChangeType change_type = change_type_i;
+	NMDeviceBtPrivate *priv;
+
+	/* bluez doesn't notify us when the connection disconnects.
+	 * Neither does NMManager (or NMDevice) tell us when the ip-ifindex goes away.
+	 * This is horrible, and should be improved. For now, watch the link ourself... */
+
+	if (NM_IN_SET (change_type, NM_PLATFORM_SIGNAL_CHANGED,
+	                            NM_PLATFORM_SIGNAL_REMOVED)) {
+		priv = NM_DEVICE_BT_GET_PRIVATE (self);
+		if (priv->connect_watch_link_idle_id == 0)
+			priv->connect_watch_link_idle_id = g_idle_add (connect_watch_link_idle_cb, self);
+	}
+}
 
-	_LOGI (LOGD_BT,
-	       "Activation: (bluetooth) Stage 2 of 5 (Device Configure) successful. Will connect via %s.",
-	       dun ? "DUN" : (pan ? "PAN" : "unknown"));
+static gboolean
+connect_wait_modem_timeout (gpointer user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 
-	nm_clear_g_source (&priv->timeout_id);
-	nm_clear_g_cancellable (&priv->cancellable);
+	/* since this timeout is longer than the connect timeout, we must have already
+	 * hit the connect-timeout first or being connected. */
+	nm_assert (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_COMPLETED);
 
-	if (pan) {
-		/* Bluez says we're connected now.  Start IP config. */
-		nm_device_activate_schedule_stage3_ip_config_start (device);
-	} else if (dun) {
-		/* Wait for ModemManager to find the modem */
-		priv->timeout_id = g_timeout_add_seconds (30, modem_find_timeout, self);
+	priv->connect_wait_modem_id = 0;
+	nm_clear_g_cancellable (&priv->connect_bz_cancellable);
 
-		_LOGI (LOGD_BT | LOGD_MB,
-		       "Activation: (bluetooth) Stage 2 of 5 (Device Configure) waiting for modem to appear.");
-	} else
-		g_assert_not_reached ();
+	if (priv->modem)
+		_LOGD (LOGD_BT, "timeout connecting modem for DUN connection");
+	else
+		_LOGD (LOGD_BT, "timeout finding modem for DUN connection");
+
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_FAILED,
+	                         NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
+	return G_SOURCE_REMOVE;
 }
 
 static void
-bluez_connect_cb (NMBluezDevice *bt_device,
-                  const char *device_name,
-                  GError *error,
-                  gpointer user_data)
+connect_bz_cb (NMBluezManager *bz_mgr,
+               gboolean is_complete,
+               const char *device_name,
+               GError *error,
+               gpointer user_data)
 {
-	gs_unref_object NMDeviceBt *self = user_data;
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	NMDeviceBt *self;
+	NMDeviceBtPrivate *priv;
+	char sbuf[100];
 
 	if (nm_utils_error_is_cancelled (error, FALSE))
 		return;
 
-	nm_clear_g_source (&priv->timeout_id);
-	g_clear_object (&priv->cancellable);
+	self = user_data;
+	priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	nm_assert (nm_device_is_activating (NM_DEVICE (self)));
+	nm_assert (NM_IN_SET ((NMBluetoothCapabilities) priv->connect_bt_type, NM_BT_CAPABILITY_DUN,
+	                                                                       NM_BT_CAPABILITY_NAP));
 
-	if (!nm_device_is_activating (NM_DEVICE (self)))
+	if (!is_complete) {
+		nm_assert (priv->connect_bt_type == NM_BT_CAPABILITY_DUN);
+		nm_assert (device_name);
+		nm_assert (!error);
+
+		if (!nm_streq0 (priv->connect_rfcomm_iface, device_name)) {
+			nm_assert (!priv->connect_rfcomm_iface);
+			_LOGD (LOGD_BT, "DUN is still connecting but got serial port \"%s\" to claim modem", device_name);
+			g_free (priv->connect_rfcomm_iface);
+			priv->connect_rfcomm_iface = g_strdup (device_name);
+		}
 		return;
+	}
+
+	g_clear_object (&priv->connect_bz_cancellable);
 
 	if (!device_name) {
-		_LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message);
+		_LOGW (LOGD_BT, "%s connect request failed: %s",
+		       nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)),
+		       error->message);
 		nm_device_state_changed (NM_DEVICE (self),
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_BT_FAILED);
 		return;
 	}
 
-	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
-		g_free (priv->rfcomm_iface);
-		priv->rfcomm_iface = g_strdup (device_name);
-	} else if (priv->bt_type == NM_BT_CAPABILITY_NAP) {
+	_LOGD (LOGD_BT, "%s connect request successful (%s)",
+	       nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)),
+	       device_name);
+
+	if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
+		if (!nm_streq0 (priv->connect_rfcomm_iface, device_name)) {
+			nm_assert_not_reached ();
+			g_free (priv->connect_rfcomm_iface);
+			priv->connect_rfcomm_iface = g_strdup (device_name);
+		}
+	} else {
+		nm_assert (priv->connect_bt_type == NM_BT_CAPABILITY_NAP);
 		if (!nm_device_set_ip_iface (NM_DEVICE (self), device_name)) {
 			_LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device_name);
 			nm_device_state_changed (NM_DEVICE (self),
@@ -798,107 +885,121 @@ bluez_connect_cb (NMBluezDevice *bt_device,
 			                         NM_DEVICE_STATE_REASON_BT_FAILED);
 			return;
 		}
+		priv->connect_watch_link_id = g_signal_connect (nm_device_get_platform (NM_DEVICE (self)),
+		                                                NM_PLATFORM_SIGNAL_LINK_CHANGED,
+		                                                G_CALLBACK (connect_watch_link_cb),
+		                                                self);
 	}
 
-	_LOGD (LOGD_BT, "connect request successful");
-
-	/* Stage 3 gets scheduled when Bluez says we're connected */
-	priv->have_iface = TRUE;
-	check_connect_continue (self);
-}
-
-static void
-bluez_connected_changed (NMBluezDevice *bt_device,
-                         GParamSpec *pspec,
-                         NMDevice *device)
-{
-	NMDeviceBt *self = NM_DEVICE_BT (device);
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	gboolean connected;
-	NMDeviceState state;
-
-	state = nm_device_get_state (device);
-	connected = nm_bluez_device_get_connected (bt_device);
-	if (connected) {
-		if (state == NM_DEVICE_STATE_CONFIG) {
-			_LOGD (LOGD_BT, "connected to the device");
-
-			priv->connected = TRUE;
-			check_connect_continue (self);
-		}
-	} else {
-		gboolean fail = FALSE;
-
-		/* Bluez says we're disconnected from the device.  Suck. */
-
-		if (nm_device_is_activating (device)) {
-			_LOGI (LOGD_BT, "Activation: (bluetooth) bluetooth link disconnected.");
-			fail = TRUE;
-		} else if (state == NM_DEVICE_STATE_ACTIVATED) {
-			_LOGI (LOGD_BT, "bluetooth link disconnected.");
-			fail = TRUE;
-		}
-
-		if (fail) {
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CARRIER);
-			priv->connected = FALSE;
-		}
+	if (!priv->is_connected) {
+		/* we got the callback from NMBluezManager with succes. We actually should be
+		 * connected and this line shouldn't be reached. */
+		nm_assert_not_reached ();
+		_LOGE (LOGD_BT, "bluetooth is unexpectedly not in connected state");
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+		return;
 	}
-}
 
-static gboolean
-bt_connect_timeout (gpointer user_data)
-{
-	NMDeviceBt *self = NM_DEVICE_BT (user_data);
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-
-	_LOGD (LOGD_BT, "initial connection timed out");
-
-	priv->timeout_id = 0;
-	nm_clear_g_cancellable (&priv->cancellable);
-
-	nm_device_state_changed (NM_DEVICE (self),
-	                         NM_DEVICE_STATE_FAILED,
-	                         NM_DEVICE_STATE_REASON_BT_FAILED);
-	return FALSE;
+	priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+	nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+act_stage1_prepare (NMDevice *device,
+                    NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceBt *self = NM_DEVICE_BT (device);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gs_free_error GError *error = NULL;
 	NMConnection *connection;
 
 	connection = nm_device_get_applied_connection (device);
 	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
-	priv->bt_type = get_connection_bt_type (connection);
-	if (priv->bt_type == NM_BT_CAPABILITY_NONE) {
-		// FIXME: set a reason code
+	priv->connect_bt_type = get_connection_bt_type (connection);
+	if (priv->connect_bt_type == NM_BT_CAPABILITY_NONE) {
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_BT_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	if (priv->bt_type == NM_BT_CAPABILITY_DUN && !priv->mm_running) {
+	if (   priv->connect_bt_type == NM_BT_CAPABILITY_DUN
+	    && !priv->mm_running) {
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	_LOGD (LOGD_BT, "requesting connection to the device");
+	if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_PENDING)
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+	else if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_INIT) {
+		gs_unref_object GCancellable *cancellable = NULL;
+		char sbuf[100];
+
+		_LOGD (LOGD_BT, "connecting to %s bluetooth device",
+		       nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)));
+
+		cancellable = g_cancellable_new ();
+
+		if (!nm_bluez_manager_connect (priv->bz_mgr,
+		                               priv->dbus_path,
+		                               priv->connect_bt_type,
+		                               30000,
+		                               cancellable,
+		                               connect_bz_cb,
+		                               self,
+		                               &error)) {
+			_LOGD (LOGD_BT, "cannot connect to bluetooth device: %s", error->message);
+			*out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		}
+
+		priv->connect_bz_cancellable = g_steal_pointer (&cancellable);
+		priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_PENDING;
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+	}
+
+	if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
+		if (!priv->modem) {
+			gs_free NMModem **modems = NULL;
+			guint i, n;
+
+			if (priv->connect_wait_modem_id == 0)
+				priv->connect_wait_modem_id = g_timeout_add_seconds (30, connect_wait_modem_timeout, self);
+
+			modems = nm_modem_manager_get_modems (priv->modem_manager, &n);
+			for (i = 0; i < n; i++) {
+				if (modem_try_claim (self, modems[i]))
+					break;
+			}
+			if (!priv->modem)
+				return NM_ACT_STAGE_RETURN_POSTPONE;
+		}
+
+		if (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_PENDING)
+			return NM_ACT_STAGE_RETURN_POSTPONE;
+		if (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_INIT) {
+			priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_PENDING;
+			return nm_modem_act_stage1_prepare (priv->modem,
+			                                    nm_device_get_act_request (NM_DEVICE (self)),
+			                                    out_failure_reason);
+		}
+	}
 
-	nm_clear_g_source (&priv->timeout_id);
-	nm_clear_g_cancellable (&priv->cancellable);
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
 
-	priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device);
-	priv->cancellable = g_cancellable_new ();
+static NMActStageReturn
+act_stage2_config (NMDevice *device,
+                   NMDeviceStateReason *out_failure_reason)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 
-	nm_bluez_device_connect_async (priv->bt_device,
-	                               priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP),
-	                               priv->cancellable,
-	                               bluez_connect_cb,
-	                               g_object_ref (self));
+	if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN)
+		nm_modem_act_stage2_config (priv->modem);
 
-	return NM_ACT_STAGE_RETURN_POSTPONE;
+	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 static NMActStageReturn
@@ -907,11 +1008,11 @@ act_stage3_ip_config_start (NMDevice *device,
                             gpointer *out_config,
                             NMDeviceStateReason *out_failure_reason)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 
 	nm_assert_addr_family (addr_family);
 
-	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+	if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
 		if (addr_family == AF_INET) {
 			return nm_modem_stage3_ip4_config_start (priv->modem,
 			                                         device,
@@ -930,15 +1031,17 @@ act_stage3_ip_config_start (NMDevice *device,
 static void
 deactivate (NMDevice *device)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 
-	priv->have_iface = FALSE;
-	priv->connected = FALSE;
+	nm_clear_g_signal_handler (nm_device_get_platform (device), &priv->connect_watch_link_id);
+	nm_clear_g_source (&priv->connect_watch_link_idle_id);
+	priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_INIT;
+	nm_clear_g_source (&priv->connect_wait_modem_id);
+	nm_clear_g_cancellable (&priv->connect_bz_cancellable);
 
-	nm_clear_g_source (&priv->timeout_id);
-	nm_clear_g_cancellable (&priv->cancellable);
+	priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_INIT;
 
-	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+	if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
 		if (priv->modem) {
 			nm_modem_deactivate (priv->modem, device);
 
@@ -952,22 +1055,53 @@ deactivate (NMDevice *device)
 		}
 	}
 
-	if (priv->bt_type != NM_BT_CAPABILITY_NONE)
-		nm_bluez_device_disconnect (priv->bt_device);
-
-	priv->bt_type = NM_BT_CAPABILITY_NONE;
+	if (priv->connect_bt_type != NM_BT_CAPABILITY_NONE) {
+		priv->connect_bt_type = NM_BT_CAPABILITY_NONE;
+		nm_bluez_manager_disconnect (priv->bz_mgr, priv->dbus_path);
+	}
 
-	g_free (priv->rfcomm_iface);
-	priv->rfcomm_iface = NULL;
+	nm_clear_g_free (&priv->connect_rfcomm_iface);
 
 	if (NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate)
 		NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate (device);
 }
 
-static void
-bluez_device_removed (NMBluezDevice *bdev, gpointer user_data)
+void
+_nm_device_bt_notify_removed (NMDeviceBt *self)
+{
+	g_signal_emit_by_name (self, NM_DEVICE_REMOVED);
+}
+
+/*****************************************************************************/
+
+gboolean
+_nm_device_bt_for_same_device (NMDeviceBt *self,
+                               const char *dbus_path,
+                               const char *bdaddr,
+                               const char *name,
+                               NMBluetoothCapabilities capabilities)
 {
-	g_signal_emit_by_name (NM_DEVICE_BT (user_data), NM_DEVICE_REMOVED);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	return    nm_streq (priv->dbus_path, dbus_path)
+	       && nm_streq (priv->bdaddr, bdaddr)
+	       && capabilities == priv->capabilities
+	       && (!name || nm_streq (priv->name, name));
+}
+
+void
+_nm_device_bt_notify_set_name (NMDeviceBt *self, const char *name)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	nm_assert (name);
+
+	if (!nm_streq (priv->name, name)) {
+		_LOGT (LOGD_BT, "set-name: %s", name);
+		g_free (priv->name);
+		priv->name = g_strdup (name);
+		_notify (self, PROP_BT_NAME);
+	}
 }
 
 /*****************************************************************************/
@@ -1028,9 +1162,6 @@ get_property (GObject *object, guint prop_id,
 	case PROP_BT_CAPABILITIES:
 		g_value_set_uint (value, priv->capabilities);
 		break;
-	case PROP_BT_DEVICE:
-		g_value_set_object (value, priv->bt_device);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1044,19 +1175,32 @@ set_property (GObject *object, guint prop_id,
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
 
 	switch (prop_id) {
+	case PROP_BT_BZ_MGR:
+		/* construct-only */
+		priv->bz_mgr = g_object_ref (g_value_get_pointer (value));
+		nm_assert (NM_IS_BLUEZ_MANAGER (priv->bz_mgr));
+		break;
+	case PROP_BT_DBUS_PATH:
+		/* construct-only */
+		priv->dbus_path = g_value_dup_string (value);
+		nm_assert (priv->dbus_path);
+		break;
+	case PROP_BT_BDADDR:
+		/* construct-only */
+		priv->bdaddr = g_value_dup_string (value);
+		nm_assert (priv->bdaddr);
+		break;
 	case PROP_BT_NAME:
 		/* construct-only */
 		priv->name = g_value_dup_string (value);
+		nm_assert (priv->name);
 		break;
 	case PROP_BT_CAPABILITIES:
 		/* construct-only */
 		priv->capabilities = g_value_get_uint (value);
-		break;
-	case PROP_BT_DEVICE:
-		/* construct-only */
-		priv->bt_device = g_value_dup_object (value);
-		if (!priv->bt_device)
-			g_return_if_reached ();
+		nm_assert (NM_IN_SET ((NMBluetoothCapabilities) priv->capabilities, NM_BT_CAPABILITY_DUN,
+		                                                                    NM_BT_CAPABILITY_NAP,
+		                                                                    NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1076,7 +1220,6 @@ constructed (GObject *object)
 {
 	NMDeviceBt *self = NM_DEVICE_BT (object);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	const char *my_hwaddr;
 
 	G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object);
 
@@ -1085,74 +1228,69 @@ constructed (GObject *object)
 	nm_modem_manager_name_owner_ref (priv->modem_manager);
 
 	g_signal_connect (priv->modem_manager,
+	                  NM_MODEM_MANAGER_MODEM_ADDED,
+	                  G_CALLBACK (mm_modem_added_cb),
+	                  self);
+
+	g_signal_connect (priv->modem_manager,
 	                  "notify::"NM_MODEM_MANAGER_NAME_OWNER,
 	                  G_CALLBACK (mm_name_owner_changed_cb),
 	                  self);
 
-	if (priv->bt_device) {
-		/* Watch for BT device property changes */
-		g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED,
-		                  G_CALLBACK (bluez_connected_changed),
-		                  object);
-		g_signal_connect (priv->bt_device, NM_BLUEZ_DEVICE_REMOVED,
-		                  G_CALLBACK (bluez_device_removed), object);
-	}
-
-	my_hwaddr = nm_device_get_hw_address (NM_DEVICE (object));
-	if (my_hwaddr)
-		priv->bdaddr = g_strdup (my_hwaddr);
-	else
-		g_warn_if_reached ();
-
 	set_mm_running (self);
 }
 
-NMDevice *
-nm_device_bt_new (NMBluezDevice *bt_device,
-                  const char *udi,
+NMDeviceBt *
+nm_device_bt_new (NMBluezManager *bz_mgr,
+                  const char *dbus_path,
                   const char *bdaddr,
                   const char *name,
-                  guint32 capabilities)
+                  NMBluetoothCapabilities capabilities)
 {
-	g_return_val_if_fail (udi != NULL, NULL);
-	g_return_val_if_fail (bdaddr != NULL, NULL);
-	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (bz_mgr), NULL);
+	g_return_val_if_fail (dbus_path, NULL);
+	g_return_val_if_fail (bdaddr, NULL);
+	g_return_val_if_fail (name, NULL);
 	g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL);
-	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (bt_device), NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT,
-	                                  NM_DEVICE_UDI, udi,
-	                                  NM_DEVICE_IFACE, bdaddr,
-	                                  NM_DEVICE_DRIVER, "bluez",
-	                                  NM_DEVICE_PERM_HW_ADDRESS, bdaddr,
-	                                  NM_DEVICE_BT_DEVICE, bt_device,
-	                                  NM_DEVICE_BT_NAME, name,
-	                                  NM_DEVICE_BT_CAPABILITIES, capabilities,
-	                                  NM_DEVICE_TYPE_DESC, "Bluetooth",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
-	                                  NULL);
+
+	return g_object_new (NM_TYPE_DEVICE_BT,
+	                     NM_DEVICE_UDI, dbus_path,
+	                     NM_DEVICE_IFACE, bdaddr,
+	                     NM_DEVICE_DRIVER, "bluez",
+	                     NM_DEVICE_PERM_HW_ADDRESS, bdaddr,
+	                     NM_DEVICE_BT_BDADDR, bdaddr,
+	                     NM_DEVICE_BT_BZ_MGR, bz_mgr,
+	                     NM_DEVICE_BT_CAPABILITIES, (guint) capabilities,
+	                     NM_DEVICE_BT_DBUS_PATH, dbus_path,
+	                     NM_DEVICE_BT_NAME, name,
+	                     NM_DEVICE_TYPE_DESC, "Bluetooth",
+	                     NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
+	                     NULL);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
+	NMDeviceBt *self = NM_DEVICE_BT (object);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 
-	nm_clear_g_source (&priv->timeout_id);
-	nm_clear_g_cancellable (&priv->cancellable);
+	nm_clear_g_signal_handler (nm_device_get_platform (NM_DEVICE (self)), &priv->connect_watch_link_id);
+	nm_clear_g_source (&priv->connect_watch_link_idle_id);
 
-	g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
+	nm_clear_g_source (&priv->connect_wait_modem_id);
+	nm_clear_g_cancellable (&priv->connect_bz_cancellable);
 
 	if (priv->modem_manager) {
-		g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), object);
+		g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), self);
 		nm_modem_manager_name_owner_unref (priv->modem_manager);
 		g_clear_object (&priv->modem_manager);
 	}
 
-	modem_cleanup (NM_DEVICE_BT (object));
-	g_clear_object (&priv->bt_device);
+	modem_cleanup (self);
 
 	G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object);
+
+	g_clear_object (&priv->bz_mgr);
 }
 
 static void
@@ -1160,7 +1298,8 @@ finalize (GObject *object)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
 
-	g_free (priv->rfcomm_iface);
+	g_free (priv->connect_rfcomm_iface);
+	g_free (priv->dbus_path);
 	g_free (priv->name);
 	g_free (priv->bdaddr);
 
@@ -1202,17 +1341,34 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->can_auto_connect = can_auto_connect;
 	device_class->deactivate = deactivate;
+	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->act_stage2_config = act_stage2_config;
 	device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->check_connection_available = check_connection_available;
 	device_class->complete_connection = complete_connection;
 	device_class->is_available = is_available;
-	device_class->component_added = component_added;
 	device_class->get_configured_mtu = nm_modem_get_configured_mtu;
 
 	device_class->state_changed = device_state_changed;
 
+	obj_properties[PROP_BT_BZ_MGR] =
+	     g_param_spec_pointer (NM_DEVICE_BT_BZ_MGR, "", "",
+	                           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+	                           G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_BT_BDADDR] =
+	     g_param_spec_string (NM_DEVICE_BT_BDADDR, "", "",
+	                          NULL,
+	                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_BT_DBUS_PATH] =
+	     g_param_spec_string (NM_DEVICE_BT_DBUS_PATH, "", "",
+	                          NULL,
+	                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_BT_NAME] =
 	     g_param_spec_string (NM_DEVICE_BT_NAME, "", "",
 	                          NULL,
@@ -1225,12 +1381,6 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                        G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_BT_DEVICE] =
-	     g_param_spec_object (NM_DEVICE_BT_DEVICE, "", "",
-	                          NM_TYPE_BLUEZ_DEVICE,
-	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-	                          G_PARAM_STATIC_STRINGS);
-
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[PPP_STATS] =
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index 6c8a5773..ecb5781b 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 Red Hat, Inc.
  */
 
@@ -21,7 +7,6 @@
 #define __NETWORKMANAGER_DEVICE_BT_H__
 
 #include "devices/nm-device.h"
-#include "nm-bluez-device.h"
 
 #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))
@@ -30,9 +15,11 @@
 #define NM_IS_DEVICE_BT_CLASS(klass)        (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_BT))
 #define NM_DEVICE_BT_GET_CLASS(obj)         (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
 
-#define NM_DEVICE_BT_NAME         "name"
+#define NM_DEVICE_BT_BDADDR       "bt-bdaddr"
+#define NM_DEVICE_BT_BZ_MGR       "bt-bz-mgr"
 #define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
-#define NM_DEVICE_BT_DEVICE       "bt-device"
+#define NM_DEVICE_BT_DBUS_PATH    "bt-dbus-path"
+#define NM_DEVICE_BT_NAME         "bt-name"
 
 #define NM_DEVICE_BT_PPP_STATS    "ppp-stats"
 
@@ -41,13 +28,21 @@ typedef struct _NMDeviceBtClass NMDeviceBtClass;
 
 GType nm_device_bt_get_type (void);
 
-NMDevice *nm_device_bt_new (NMBluezDevice *bt_device,
-                            const char *udi,
-                            const char *bdaddr,
-                            const char *name,
-                            guint32 capabilities);
+struct _NMBluezManager;
 
-guint32 nm_device_bt_get_capabilities (NMDeviceBt *device);
+NMDeviceBt *nm_device_bt_new (struct _NMBluezManager *bz_mgr,
+                              const char *dbus_path,
+                              const char *bdaddr,
+                              const char *name,
+                              NMBluetoothCapabilities capabilities);
+
+gboolean _nm_device_bt_for_same_device (NMDeviceBt *device,
+                                        const char *dbus_path,
+                                        const char *bdaddr,
+                                        const char *name,
+                                        NMBluetoothCapabilities capabilities);
+
+NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *device);
 
 struct _NMModem;
 
@@ -55,4 +50,11 @@ gboolean nm_device_bt_modem_added (NMDeviceBt *device,
                                    struct _NMModem *modem,
                                    const char *driver);
 
+void _nm_device_bt_notify_removed (NMDeviceBt *self);
+
+void _nm_device_bt_notify_set_name (NMDeviceBt *self, const char *name);
+
+void _nm_device_bt_notify_set_connected (NMDeviceBt *self,
+                                         gboolean connected);
+
 #endif /* __NETWORKMANAGER_DEVICE_BT_H__ */
diff --git a/src/devices/bluetooth/tests/nm-bt-test.c b/src/devices/bluetooth/tests/nm-bt-test.c
new file mode 100644
index 00000000..02cfd228
--- /dev/null
+++ b/src/devices/bluetooth/tests/nm-bt-test.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include "nm-default.h"
+
+#include <glib-unix.h>
+
+#include "devices/bluetooth/nm-bluez5-dun.h"
+
+#include "nm-test-utils-core.h"
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_BT
+#define _NMLOG(level, ...) \
+	nm_log ((level), _NMLOG_DOMAIN, \
+	        NULL, NULL, \
+	        "bt%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+	        NM_PRINT_FMT_QUOTED (gl.argv_cmd, "[", gl.argv_cmd, "]", "") \
+	        _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+struct {
+	int argc;
+	const char *const*argv;
+	const char *argv_cmd;
+	GMainLoop *loop;
+} gl;
+
+typedef struct _MainCmdInfo {
+	const char *name;
+	int (*main_func) (const struct _MainCmdInfo *main_cmd_info);
+} MainCmdInfo;
+
+/*****************************************************************************/
+
+#if WITH_BLUEZ5_DUN
+
+typedef struct {
+	NMBluez5DunContext *dun_context;
+	GCancellable *cancellable;
+	guint timeout_id;
+	guint sig_term_id;
+	guint sig_int_id;
+} DunConnectData;
+
+static void
+_dun_connect_cb (NMBluez5DunContext *context,
+                 const char *rfcomm_dev,
+                 GError *error,
+                 gpointer user_data)
+{
+	DunConnectData *dun_connect_data = user_data;
+
+	g_assert (dun_connect_data);
+	g_assert (!dun_connect_data->dun_context);
+	g_assert ((!!error) != (!!rfcomm_dev));
+
+	if (rfcomm_dev && !context) {
+		_LOGI ("dun-connect notifies path \"%s\". Wait longer...", rfcomm_dev);
+		return;
+	}
+
+	if (rfcomm_dev) {
+		g_assert (context);
+		_LOGI ("dun-connect completed with path \"%s\"", rfcomm_dev);
+	} else {
+		g_assert (!context);
+		_LOGI ("dun-connect failed with error: %s", error->message);
+	}
+
+	dun_connect_data->dun_context = context;
+
+	g_main_loop_quit (gl.loop);
+}
+
+static void
+_dun_notify_tty_hangup_cb (NMBluez5DunContext *context,
+                           gpointer user_data)
+{
+	_LOGI ("dun-connect: notified TTY hangup");
+}
+
+static gboolean
+_timeout_cb (gpointer user_data)
+{
+	DunConnectData *dun_connect_data = user_data;
+
+	_LOGI ("timeout");
+	dun_connect_data->timeout_id = 0;
+	if (dun_connect_data->cancellable)
+		g_cancellable_cancel (dun_connect_data->cancellable);
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_sig_xxx_cb (DunConnectData *dun_connect_data, int sigid)
+{
+	_LOGI ("signal %s received", sigid == SIGTERM ? "SIGTERM" : "SIGINT");
+	g_main_loop_quit (gl.loop);
+	return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+_sig_term_cb (gpointer user_data)
+{
+	return _sig_xxx_cb (user_data, SIGTERM);
+}
+
+static gboolean
+_sig_int_cb (gpointer user_data)
+{
+	return _sig_xxx_cb (user_data, SIGINT);
+}
+#endif
+
+static int
+do_dun_connect (const MainCmdInfo *main_cmd_info)
+{
+#if WITH_BLUEZ5_DUN
+	gs_unref_object GCancellable *cancellable = NULL;
+	gs_free_error GError *error = NULL;
+	const char *adapter;
+	const char *remote;
+	DunConnectData dun_connect_data = { };
+
+	if (gl.argc < 4) {
+		_LOGE ("missing arguments \"adapter\" and \"remote\"");
+		return -1;
+	}
+
+	adapter = gl.argv[2];
+	remote = gl.argv[3];
+
+	cancellable = g_cancellable_new ();
+	dun_connect_data.cancellable = cancellable;
+
+	if (!nm_bluez5_dun_connect (adapter,
+	                            remote,
+	                            cancellable,
+	                            _dun_connect_cb,
+	                            &dun_connect_data,
+	                            _dun_notify_tty_hangup_cb,
+	                            &dun_connect_data,
+	                            &error)) {
+		_LOGE ("connect failed to start: %s", error->message);
+		return -1;
+	}
+
+	dun_connect_data.timeout_id = g_timeout_add (60000, _timeout_cb, &dun_connect_data);
+
+	g_main_loop_run (gl.loop);
+
+	nm_clear_g_source (&dun_connect_data.timeout_id);
+
+	if (dun_connect_data.dun_context) {
+
+		dun_connect_data.sig_term_id = g_unix_signal_add (SIGTERM, _sig_term_cb, &dun_connect_data);
+		dun_connect_data.sig_int_id = g_unix_signal_add (SIGINT, _sig_int_cb, &dun_connect_data);
+
+		g_main_loop_run (gl.loop);
+
+		nm_clear_g_source (&dun_connect_data.sig_term_id);
+		nm_clear_g_source (&dun_connect_data.sig_int_id);
+
+		nm_bluez5_dun_disconnect (g_steal_pointer (&dun_connect_data.dun_context));
+	}
+
+	return 0;
+#else
+	_LOGE ("compiled without bluetooth DUN support");
+	return 1;
+#endif
+}
+
+/*****************************************************************************/
+
+NMTST_DEFINE ();
+
+int
+main (int argc, char **argv)
+{
+	static const MainCmdInfo main_cmd_infos[] = {
+		{ .name = "dun-connect", .main_func = do_dun_connect, },
+	};
+	int exit_code = 0;
+	guint i;
+
+	if (!g_getenv ("G_MESSAGES_DEBUG"))
+		g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+
+	nmtst_init_with_logging (&argc, &argv, "DEBUG", "ALL");
+
+	nm_logging_init (NULL, TRUE);
+
+	gl.argv = (const char *const*) argv;
+	gl.argc = argc;
+	gl.loop = g_main_loop_new (NULL, FALSE);
+
+	_LOGI ("bluetooth test util start");
+
+	gl.argv_cmd = argc >= 2 ? argv[1] : NULL;
+
+	for (i = 0; i < G_N_ELEMENTS (main_cmd_infos); i++) {
+		if (nm_streq0 (main_cmd_infos[i].name, gl.argv_cmd)) {
+			_LOGD ("start \"%s\"", gl.argv_cmd);
+			exit_code = main_cmd_infos[i].main_func (&main_cmd_infos[i]);
+			_LOGD ("completed with %d", exit_code);
+			break;
+		}
+	}
+	if (gl.argv_cmd && i >= G_N_ELEMENTS (main_cmd_infos)) {
+		nm_log_err (LOGD_BT, "invalid command \"%s\"", gl.argv_cmd);
+		exit_code = -1;
+	}
+
+	nm_clear_pointer (&gl.loop, g_main_loop_unref);
+
+	return exit_code;
+}
diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c
index 036c4709..6e7a2a0f 100644
--- a/src/devices/nm-acd-manager.c
+++ b/src/devices/nm-acd-manager.c
@@ -1,16 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2015-2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h
index 53c88e4b..4afe509d 100644
--- a/src/devices/nm-acd-manager.h
+++ b/src/devices/nm-acd-manager.h
@@ -1,16 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2015-2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_ACD_MANAGER__
diff --git a/src/devices/nm-device-6lowpan.c b/src/devices/nm-device-6lowpan.c
index 7a279431..0289a360 100644
--- a/src/devices/nm-device-6lowpan.c
+++ b/src/devices/nm-device-6lowpan.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -230,20 +216,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 	              NULL);
 }
 
-static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
-{
-	NMActStageReturn ret;
-
-	ret = NM_DEVICE_CLASS (nm_device_6lowpan_parent_class)->act_stage1_prepare (dev, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	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
@@ -273,7 +245,7 @@ nm_device_6lowpan_class_init (NMDevice6LowpanClass *klass)
 	device_class->connection_type_check_compatible = NM_SETTING_6LOWPAN_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_6LOWPAN);
 
-	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->complete_connection = complete_connection;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
diff --git a/src/devices/nm-device-6lowpan.h b/src/devices/nm-device-6lowpan.h
index 86edf4af..7d427235 100644
--- a/src/devices/nm-device-6lowpan.h
+++ b/src/devices/nm-device-6lowpan.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_6LOWPAN_H__
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 50494526..c6ecb2e8 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -1,20 +1,6 @@
-/* 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 2011 - 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -213,10 +199,10 @@ set_simple_option (NMDevice *device,
 	set_bond_attr (device, mode, opt, value);
 }
 
-static NMActStageReturn
-apply_bonding_config (NMDevice *device)
+static gboolean
+apply_bonding_config (NMDeviceBond *self)
 {
-	NMDeviceBond *self = NM_DEVICE_BOND (device);
+	NMDevice *device = NM_DEVICE (self);
 	NMSettingBond *s_bond;
 	int ifindex = nm_device_get_ifindex (device);
 	const char *mode_str, *value;
@@ -239,7 +225,7 @@ apply_bonding_config (NMDevice *device)
 
 	s_bond = nm_device_get_applied_setting (device, NM_TYPE_SETTING_BOND);
 
-	g_return_val_if_fail (s_bond, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (s_bond, FALSE);
 
 	mode_str = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
 	if (!mode_str)
@@ -248,7 +234,7 @@ apply_bonding_config (NMDevice *device)
 	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;
+		return FALSE;
 	}
 
 	/* Set mode first, as some other options (e.g. arp_interval) are valid
@@ -334,24 +320,26 @@ apply_bonding_config (NMDevice *device)
 	else
 		set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA);
 
-	return NM_ACT_STAGE_RETURN_SUCCESS;
+	return TRUE;
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
+	NMDeviceBond *self = NM_DEVICE_BOND (device);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 
-	ret = NM_DEVICE_CLASS (nm_device_bond_parent_class)->act_stage1_prepare (dev, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	/* Interface must be down to set bond options */
-	nm_device_take_down (dev, TRUE);
-	ret = apply_bonding_config (dev);
-	if (ret != NM_ACT_STAGE_RETURN_FAILURE)
-		ret = nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE);
-	nm_device_bring_up (dev, TRUE, NULL);
+	nm_device_take_down (device, TRUE);
+	if (!apply_bonding_config (self))
+		ret = NM_ACT_STAGE_RETURN_FAILURE;
+	else {
+		if (!nm_device_hw_addr_set_cloned (device,
+		                                   nm_device_get_applied_connection (device),
+		                                   FALSE))
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
+	}
+	nm_device_bring_up (device, TRUE, NULL);
 
 	return ret;
 }
@@ -416,10 +404,12 @@ release_slave (NMDevice *device,
 	int ifindex_slave;
 	int ifindex;
 
-	ifindex = nm_device_get_ifindex (device);
-	if (   ifindex <= 0
-	    || !nm_platform_link_get (nm_device_get_platform (device), ifindex))
-		configure = FALSE;
+	if (configure) {
+		ifindex = nm_device_get_ifindex (device);
+		if (   ifindex <= 0
+		    || !nm_platform_link_get (nm_device_get_platform (device), ifindex))
+			configure = FALSE;
+	}
 
 	ifindex_slave = nm_device_get_ip_ifindex (slave);
 
diff --git a/src/devices/nm-device-bond.h b/src/devices/nm-device-bond.h
index 9448950b..7964f2a2 100644
--- a/src/devices/nm-device-bond.h
+++ b/src/devices/nm-device-bond.h
@@ -1,20 +1,6 @@
-/* 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 2012 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_BOND_H__
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index 91d824b3..72a8ce2b 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -1,20 +1,6 @@
-/* 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 2011 - 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 - 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -36,7 +22,9 @@ _LOG_DECLARE_SELF(NMDeviceBridge);
 
 struct _NMDeviceBridge {
 	NMDevice parent;
+	GCancellable *bt_cancellable;
 	bool vlan_configured:1;
+	bool bt_registered:1;
 };
 
 struct _NMDeviceBridgeClass {
@@ -64,6 +52,7 @@ check_connection_available (NMDevice *device,
                             const char *specific_object,
                             GError **error)
 {
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
 	NMSettingBluetooth *s_bt;
 
 	if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_available (device, connection, flags, specific_object, error))
@@ -80,13 +69,18 @@ check_connection_available (NMDevice *device,
 		}
 
 		bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
-		if (!nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server, bdaddr)) {
+		if (!nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server,
+		                                                bdaddr,
+		                                                  (   self->bt_cancellable
+		                                                   || self->bt_registered)
+		                                                ? device
+		                                                : NULL)) {
 			if (bdaddr)
 				nm_utils_error_set (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
-				                    "not suitable NAP device \"%s\" available", bdaddr);
+				                    "no suitable NAP device \"%s\" available", bdaddr);
 			else
 				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
-				                            "not suitable NAP device available");
+				                            "no suitable NAP device available");
 			return FALSE;
 		}
 	}
@@ -225,7 +219,7 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool
 	GParamSpec *pspec;
 	GValue val = G_VALUE_INIT;
 	guint32 uval = 0;
-	gs_free char *value = NULL;
+	char value[100];
 
 	g_assert (setting);
 
@@ -258,10 +252,10 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool
 		if (option->user_hz_compensate)
 			uval *= 100;
 	} else
-		g_assert_not_reached ();
+		nm_assert_not_reached ();
 	g_value_unset (&val);
 
-	value = g_strdup_printf ("%u", uval);
+	nm_sprintf_buf (value, "%u", uval);
 	if (slave)
 		nm_platform_sysctl_slave_set_option (nm_device_get_platform (device), ifindex, option->sysname, value);
 	else
@@ -497,27 +491,16 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge)
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
-	NMActStageReturn ret;
 	NMConnection *connection;
 	NMSetting *s_bridge;
 	const Option *option;
 
-	NM_DEVICE_BRIDGE (device)->vlan_configured = FALSE;
-
-	ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	connection = nm_device_get_applied_connection (device);
 	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
 	s_bridge = (NMSetting *) nm_connection_get_setting_bridge (connection);
 	g_return_val_if_fail (s_bridge, NM_ACT_STAGE_RETURN_FAILURE);
 
-	if (!nm_device_hw_addr_set_cloned (device, connection, FALSE)) {
-		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
-
 	for (option = master_options; option->name; option++)
 		commit_option (device, s_bridge, option, FALSE);
 
@@ -529,9 +512,53 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
+static void
+_bt_register_bridge_cb (GError *error,
+                        gpointer user_data)
+{
+	NMDeviceBridge *self;
+
+	if (nm_utils_error_is_cancelled (error, FALSE))
+		return;
+
+	self = user_data;
+
+	g_clear_object (&self->bt_cancellable);
+
+	if (error) {
+		_LOGD (LOGD_DEVICE, "bluetooth NAP server failed to register bridge: %s", error->message);
+		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+		return;
+	}
+
+	nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self));
+}
+
+void
+_nm_device_bridge_notify_unregister_bt_nap (NMDevice *device,
+                                            const char *reason)
+{
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+
+	_LOGD (LOGD_DEVICE, "bluetooth NAP server unregistered from bridge: %s%s",
+	       reason,
+	       self->bt_registered ? "" : " (was no longer registered)");
+
+	nm_clear_g_cancellable (&self->bt_cancellable);
+
+	if (self->bt_registered) {
+		self->bt_registered = FALSE;
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+	}
+}
+
 static NMActStageReturn
 act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
 	NMConnection *connection;
 	NMSettingBluetooth *s_bt;
 
@@ -539,14 +566,32 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
 	s_bt = _nm_connection_get_setting_bluetooth_for_nap (connection);
 	if (s_bt) {
-		if (   !nm_bt_vtable_network_server
-		    || !nm_bt_vtable_network_server->register_bridge (nm_bt_vtable_network_server,
-		                                                      nm_setting_bluetooth_get_bdaddr (s_bt),
-		                                                      device)) {
-			/* The HCI we could use is no longer present. */
-			*out_failure_reason = NM_DEVICE_STATE_REASON_REMOVED;
+		gs_free_error GError *error = NULL;
+
+		if (!nm_bt_vtable_network_server) {
+			_LOGD (LOGD_DEVICE, "bluetooth NAP server failed because bluetooth plugin not available");
+			*out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
 			return NM_ACT_STAGE_RETURN_FAILURE;
 		}
+
+		if (self->bt_cancellable)
+			return NM_ACT_STAGE_RETURN_POSTPONE;
+
+		self->bt_cancellable = g_cancellable_new ();
+		if (!nm_bt_vtable_network_server->register_bridge (nm_bt_vtable_network_server,
+		                                                   nm_setting_bluetooth_get_bdaddr (s_bt),
+		                                                   device,
+		                                                   self->bt_cancellable,
+		                                                   _bt_register_bridge_cb,
+		                                                   device,
+		                                                   &error)) {
+			_LOGD (LOGD_DEVICE, "bluetooth NAP server failed to register bridge: %s", error->message);
+			*out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		}
+
+		self->bt_registered = TRUE;
+		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
 
 	return NM_ACT_STAGE_RETURN_SUCCESS;
@@ -555,9 +600,17 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 static void
 deactivate (NMDevice *device)
 {
-	if (nm_bt_vtable_network_server) {
-		/* always call unregister. It does nothing if the device
-		 * isn't registered as a hotspot bridge. */
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+
+	_LOGD (LOGD_DEVICE, "deactivate bridge%s",
+	       self->bt_registered ? " (registered as NAP bluetooth device)" : "");
+
+	self->vlan_configured = FALSE;
+
+	nm_clear_g_cancellable (&self->bt_cancellable);
+
+	if (self->bt_registered) {
+		self->bt_registered = FALSE;
 		nm_bt_vtable_network_server->unregister_bridge (nm_bt_vtable_network_server,
 		                                                device);
 	}
@@ -628,10 +681,12 @@ release_slave (NMDevice *device,
 	int ifindex_slave;
 	int ifindex;
 
-	ifindex = nm_device_get_ifindex (device);
-	if (   ifindex <= 0
-	    || !nm_platform_link_get (nm_device_get_platform (device), ifindex))
-		configure = FALSE;
+	if (configure) {
+		ifindex = nm_device_get_ifindex (device);
+		if (   ifindex <= 0
+		    || !nm_platform_link_get (nm_device_get_platform (device), ifindex))
+			configure = FALSE;
+	}
 
 	ifindex_slave = nm_device_get_ip_ifindex (slave);
 
@@ -758,6 +813,7 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 	device_class->master_update_slave_connection = master_update_slave_connection;
 
 	device_class->create_and_realize = create_and_realize;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->act_stage2_config = act_stage2_config;
 	device_class->deactivate = deactivate;
diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h
index bc5ed04c..fb614d40 100644
--- a/src/devices/nm-device-bridge.h
+++ b/src/devices/nm-device-bridge.h
@@ -1,20 +1,6 @@
-/* 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 2012 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_BRIDGE_H__
@@ -36,4 +22,7 @@ GType nm_device_bridge_get_type (void);
 
 extern const NMBtVTableNetworkServer *nm_bt_vtable_network_server;
 
+void _nm_device_bridge_notify_unregister_bt_nap (NMDevice *device,
+                                                 const char *reason);
+
 #endif /* __NETWORKMANAGER_DEVICE_BRIDGE_H__ */
diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c
index 9c0c3035..465a293b 100644
--- a/src/devices/nm-device-dummy.c
+++ b/src/devices/nm-device-dummy.c
@@ -1,14 +1,6 @@
-/* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -116,21 +108,6 @@ create_and_realize (NMDevice *device,
 	return TRUE;
 }
 
-static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
-{
-	NMActStageReturn ret;
-
-	ret = NM_DEVICE_CLASS (nm_device_dummy_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	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;
-}
-
 /*****************************************************************************/
 
 static void
@@ -167,7 +144,7 @@ nm_device_dummy_class_init (NMDeviceDummyClass *klass)
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->update_connection = update_connection;
-	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
 }
 
diff --git a/src/devices/nm-device-dummy.h b/src/devices/nm-device-dummy.h
index a380d366..e95be72d 100644
--- a/src/devices/nm-device-dummy.h
+++ b/src/devices/nm-device-dummy.h
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_DUMMY_H__
diff --git a/src/devices/nm-device-ethernet-utils.c b/src/devices/nm-device-ethernet-utils.c
index 1d7a060e..79673201 100644
--- a/src/devices/nm-device-ethernet-utils.c
+++ b/src/devices/nm-device-ethernet-utils.c
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2011 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/devices/nm-device-ethernet-utils.h b/src/devices/nm-device-ethernet-utils.h
index 6355c4ec..0e16f4b8 100644
--- a/src/devices/nm-device-ethernet-utils.h
+++ b/src/devices/nm-device-ethernet-utils.h
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2011 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index b2b05883..6b80c4ed 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2014 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -853,14 +839,19 @@ link_negotiation_set (NMDevice *device)
 		autoneg = nm_setting_wired_get_auto_negotiate (s_wired);
 		speed = nm_setting_wired_get_speed (s_wired);
 		duplex = link_duplex_to_platform (nm_setting_wired_get_duplex (s_wired));
-		if (!autoneg && !speed && !duplex) {
+		if (   !autoneg
+		    && !speed
+		    && !duplex) {
 			_LOGD (LOGD_DEVICE, "set-link: ignore link negotiation");
 			return;
 		}
 	}
 
-	if (!nm_platform_ethtool_get_link_settings (nm_device_get_platform (device), nm_device_get_ifindex (device),
-	                                            &link_autoneg, &link_speed, &link_duplex)) {
+	if (!nm_platform_ethtool_get_link_settings (nm_device_get_platform (device),
+	                                            nm_device_get_ifindex (device),
+	                                            &link_autoneg,
+	                                            &link_speed,
+	                                            &link_duplex)) {
 		_LOGW (LOGD_DEVICE, "set-link: unable to retrieve link negotiation");
 		return;
 	}
@@ -873,16 +864,18 @@ link_negotiation_set (NMDevice *device)
 		return;
 	}
 
-	if (autoneg && !speed && !duplex)
+	if (   autoneg
+	    && !speed
+	    && !duplex)
 		_LOGD (LOGD_DEVICE, "set-link: configure auto-negotiation");
 	else {
 		_LOGD (LOGD_DEVICE, "set-link: configure %snegotiation (%u Mbit%s - %s duplex%s)",
 		       autoneg ? "auto-" : "static ",
 		       speed ?: link_speed,
 		       speed ? "" : "*",
-		       duplex
-		         ? nm_platform_link_duplex_type_to_string (duplex)
-		         : nm_platform_link_duplex_type_to_string (link_duplex),
+		         duplex
+		       ? nm_platform_link_duplex_type_to_string (duplex)
+		       : nm_platform_link_duplex_type_to_string (link_duplex),
 		       duplex ? "" : "*");
 	}
 
@@ -903,9 +896,10 @@ pppoe_reconnect_delay (gpointer user_data)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
 	priv->pppoe_wait_id = 0;
+	priv->last_pppoe_time = 0;
 	_LOGI (LOGD_DEVICE, "PPPoE reconnect delay complete, resuming connection...");
-	nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self));
-	return FALSE;
+	nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
+	return G_SOURCE_REMOVE;
 }
 
 static NMActStageReturn
@@ -913,35 +907,33 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	NMActStageReturn ret;
-
-	ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
 
 	link_negotiation_set (device);
 
-	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
-		return NM_ACT_STAGE_RETURN_FAILURE;
-
 	/* 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
 	 * get confused and fail to negotiate the new connection. (rh #1023503)
+	 *
+	 * FIXME(shutdown): when exiting, we also need to wait before quiting,
+	 * at least for additional NM_SHUTDOWN_TIMEOUT_MS seconds because
+	 * otherwise after restart the device won't work for the first seconds.
 	 */
-	if (priv->last_pppoe_time) {
+	if (priv->last_pppoe_time != 0) {
 		gint32 delay = nm_utils_get_monotonic_timestamp_s () - priv->last_pppoe_time;
 
 		if (   delay < PPPOE_RECONNECT_DELAY
 		    && nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPPOE)) {
-			_LOGI (LOGD_DEVICE, "delaying PPPoE reconnect for %d seconds to ensure peer is ready...",
-			       delay);
-			g_assert (!priv->pppoe_wait_id);
-			priv->pppoe_wait_id = g_timeout_add_seconds (delay,
-			                                             pppoe_reconnect_delay,
-			                                             self);
+			if (priv->pppoe_wait_id == 0) {
+				_LOGI (LOGD_DEVICE, "delaying PPPoE reconnect for %d seconds to ensure peer is ready...",
+				       delay);
+				priv->pppoe_wait_id = g_timeout_add_seconds (delay,
+				                                             pppoe_reconnect_delay,
+				                                             self);
+			}
 			return NM_ACT_STAGE_RETURN_POSTPONE;
 		}
+		nm_clear_g_source (&priv->pppoe_wait_id);
 		priv->last_pppoe_time = 0;
 	}
 
@@ -1743,6 +1735,7 @@ static void
 reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_new)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceState state = nm_device_get_state (device);
 
 	NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->reapply_connection (device,
 	                                                                       con_old,
@@ -1750,8 +1743,10 @@ reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_n
 
 	_LOGD (LOGD_DEVICE, "reapplying wired settings");
 
-	link_negotiation_set (device);
-	wake_on_lan_enable (device);
+	if (state >= NM_DEVICE_STATE_PREPARE)
+		link_negotiation_set (device);
+	if (state >= NM_DEVICE_STATE_CONFIG)
+		wake_on_lan_enable (device);
 }
 
 static void
@@ -1864,6 +1859,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	device_class->new_default_connection = new_default_connection;
 
 	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->act_stage2_config = act_stage2_config;
 	device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
 	device_class->get_configured_mtu = get_configured_mtu;
diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h
index d7de519c..08962232 100644
--- a/src/devices/nm-device-ethernet.h
+++ b/src/devices/nm-device-ethernet.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index c0ec1d2e..232e69ef 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 - 2018 Red Hat, Inc.
  */
 
@@ -36,7 +22,6 @@
 
 enum {
 	DEVICE_ADDED,
-	COMPONENT_ADDED,
 	LAST_SIGNAL
 };
 
@@ -46,17 +31,6 @@ G_DEFINE_ABSTRACT_TYPE (NMDeviceFactory, nm_device_factory, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
-gboolean
-nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component)
-{
-	gboolean consumed = FALSE;
-
-	g_return_val_if_fail (NM_IS_DEVICE_FACTORY (factory), FALSE);
-
-	g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed);
-	return consumed;
-}
-
 static void
 nm_device_factory_get_supported_types (NMDeviceFactory *factory,
                                        const NMLinkType **out_link_types,
@@ -195,16 +169,8 @@ nm_device_factory_class_init (NMDeviceFactoryClass *klass)
 	signals[DEVICE_ADDED] = g_signal_new (NM_DEVICE_FACTORY_DEVICE_ADDED,
 	                                      G_OBJECT_CLASS_TYPE (object_class),
 	                                      G_SIGNAL_RUN_FIRST,
-	                                      G_STRUCT_OFFSET (NMDeviceFactoryClass, device_added),
-	                                      NULL, NULL, NULL,
+	                                      0, NULL, NULL, NULL,
 	                                      G_TYPE_NONE, 1, NM_TYPE_DEVICE);
-
-	signals[COMPONENT_ADDED] = g_signal_new (NM_DEVICE_FACTORY_COMPONENT_ADDED,
-	                                         G_OBJECT_CLASS_TYPE (object_class),
-	                                         G_SIGNAL_RUN_LAST,
-	                                         G_STRUCT_OFFSET (NMDeviceFactoryClass, component_added),
-	                                         g_signal_accumulator_true_handled, NULL, NULL,
-	                                         G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h
index 7fe371ad..7a323d49 100644
--- a/src/devices/nm-device-factory.h
+++ b/src/devices/nm-device-factory.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2014 Red Hat, Inc.
  */
 
@@ -36,7 +22,6 @@
 #define NM_IS_DEVICE_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_FACTORY))
 #define NM_DEVICE_FACTORY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactoryClass))
 
-#define NM_DEVICE_FACTORY_COMPONENT_ADDED "component-added"
 #define NM_DEVICE_FACTORY_DEVICE_ADDED    "device-added"
 
 typedef struct {
@@ -131,36 +116,6 @@ typedef struct {
 	                               NMConnection *connection,
 	                               gboolean *out_ignore);
 
-	/* Signals */
-
-	/**
-	 * device_added:
-	 * @factory: the #NMDeviceFactory
-	 * @device: the new #NMDevice subclass
-	 *
-	 * The factory emits this signal if it finds a new device by itself.
-	 */
-	void       (*device_added)    (NMDeviceFactory *factory, NMDevice *device);
-
-	/**
-	 * component_added:
-	 * @factory: the #NMDeviceFactory
-	 * @component: a new component which existing devices may wish to claim
-	 *
-	 * The factory emits this signal when an appearance of some component
-	 * native to it could be interesting to some of the already existing devices.
-	 * The devices then indicate if they took interest in claiming the component.
-	 *
-	 * For example, the WWAN factory may indicate that a new modem is available,
-	 * which an existing Bluetooth device may wish to claim. It emits a signal
-	 * passing the modem instance around to see if any device claims it.
-	 * If no device claims the component, the plugin is allowed to create a new
-	 * #NMDevice instance for that component and emit the "device-added" signal.
-	 *
-	 * Returns: %TRUE if the component was claimed by a device, %FALSE if not
-	 */
-	gboolean   (*component_added) (NMDeviceFactory *factory, GObject *component);
-
 } NMDeviceFactoryClass;
 
 GType      nm_device_factory_get_type    (void);
@@ -202,10 +157,6 @@ NMDevice * nm_device_factory_create_device (NMDeviceFactory *factory,
                                             gboolean *out_ignore,
                                             GError **error);
 
-/* For use by implementations */
-gboolean   nm_device_factory_emit_component_added (NMDeviceFactory *factory,
-                                                   GObject *component);
-
 #define NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(...) \
 	{ static NMLinkType const _link_types_declared[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; _link_types = _link_types_declared; }
 #define NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(...) \
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index 39232acf..5f9e13c3 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/devices/nm-device-generic.h b/src/devices/nm-device-generic.h
index 9c59851a..5dd90a3a 100644
--- a/src/devices/nm-device-generic.h
+++ b/src/devices/nm-device-generic.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_GENERIC_H__
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index e39329a9..9f79d9bb 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -1,20 +1,6 @@
-/* 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 2011 - 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -76,16 +62,11 @@ static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	nm_auto_close int dirfd = -1;
-	NMActStageReturn ret;
 	NMSettingInfiniband *s_infiniband;
 	char ifname_verified[IFNAMSIZ];
 	const char *transport_mode;
 	gboolean ok;
 
-	ret = NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	s_infiniband = nm_device_get_applied_setting (device, NM_TYPE_SETTING_INFINIBAND);
 
 	g_return_val_if_fail (s_infiniband, NM_ACT_STAGE_RETURN_FAILURE);
@@ -94,7 +75,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
 	dirfd = nm_platform_sysctl_open_netdir (nm_device_get_platform (device), nm_device_get_ifindex (device), ifname_verified);
 	if (dirfd < 0) {
-		if (!strcmp (transport_mode, "datagram"))
+		if (nm_streq (transport_mode, "datagram"))
 			return NM_ACT_STAGE_RETURN_SUCCESS;
 		else {
 			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_INFINIBAND_MODE);
@@ -210,6 +191,32 @@ update_connection (NMDevice *device, NMConnection *connection)
 }
 
 static gboolean
+can_reapply_change (NMDevice *device,
+                    const char *setting_name,
+                    NMSetting *s_old,
+                    NMSetting *s_new,
+                    GHashTable *diffs,
+                    GError **error)
+{
+	NMDeviceClass *device_class;
+
+	if (nm_streq (setting_name, NM_SETTING_INFINIBAND_SETTING_NAME)) {
+		return nm_device_hash_check_invalid_keys (diffs,
+		                                          NM_SETTING_INFINIBAND_SETTING_NAME,
+		                                          error,
+		                                          NM_SETTING_INFINIBAND_MTU); /* reapplied with IP config */
+	}
+
+	device_class = NM_DEVICE_CLASS (nm_device_infiniband_parent_class);
+	return device_class->can_reapply_change (device,
+	                                         setting_name,
+	                                         s_old,
+	                                         s_new,
+	                                         diffs,
+	                                         error);
+}
+
+static gboolean
 create_and_realize (NMDevice *device,
                     NMConnection *connection,
                     NMDevice *parent,
@@ -361,6 +368,7 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	device_class->connection_type_check_compatible = NM_SETTING_INFINIBAND_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_INFINIBAND);
 
+	device_class->can_reapply_change = can_reapply_change;
 	device_class->create_and_realize = create_and_realize;
 	device_class->unrealize = unrealize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
diff --git a/src/devices/nm-device-infiniband.h b/src/devices/nm-device-infiniband.h
index 55d0aadc..6f46ef50 100644
--- a/src/devices/nm-device-infiniband.h
+++ b/src/devices/nm-device-infiniband.h
@@ -1,20 +1,6 @@
-/* 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 2011 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_INFINIBAND_H__
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index f070dd5f..0becb5e5 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -967,21 +953,6 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
-static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
-{
-	NMActStageReturn ret;
-
-	ret = NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	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;
-}
-
 /*****************************************************************************/
 
 static void
@@ -1068,7 +1039,7 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 	                                                        NM_LINK_TYPE_IPIP,
 	                                                        NM_LINK_TYPE_SIT);
 
-	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->link_changed = link_changed;
 	device_class->can_reapply_change = can_reapply_change;
 	device_class->complete_connection = complete_connection;
diff --git a/src/devices/nm-device-ip-tunnel.h b/src/devices/nm-device-ip-tunnel.h
index e38d36eb..a8f30711 100644
--- a/src/devices/nm-device-ip-tunnel.h
+++ b/src/devices/nm-device-ip-tunnel.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_IP_TUNNEL_H__
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
index c45a0552..2c56e526 100644
--- a/src/devices/nm-device-logging.h
+++ b/src/devices/nm-device-logging.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
index 13798f5e..c9592a49 100644
--- a/src/devices/nm-device-macsec.c
+++ b/src/devices/nm-device-macsec.c
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -324,8 +310,10 @@ macsec_secrets_cb (NMActRequest *req,
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-	} else
-		nm_device_activate_schedule_stage1_device_prepare (device);
+		return;
+	}
+
+	nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 static void
diff --git a/src/devices/nm-device-macsec.h b/src/devices/nm-device-macsec.h
index e6919d4c..38a7dbae 100644
--- a/src/devices/nm-device-macsec.h
+++ b/src/devices/nm-device-macsec.h
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_DEVICE_MACSEC_H__
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index 964e0f61..3633f02d 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 - 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 - 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -430,21 +416,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 	              NULL);
 }
 
-static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
-{
-	NMActStageReturn ret;
-
-	ret = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	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;
-}
-
 /*****************************************************************************/
 
 static void
@@ -536,7 +507,7 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP);
 	device_class->mtu_parent_delta = 0;
 
-	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->complete_connection = complete_connection;
 	device_class->create_and_realize = create_and_realize;
diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h
index 9e76f5c9..8fcf97cd 100644
--- a/src/devices/nm-device-macvlan.h
+++ b/src/devices/nm-device-macvlan.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_MACVLAN_H__
diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c
index f2e68f73..32403ff6 100644
--- a/src/devices/nm-device-ppp.c
+++ b/src/devices/nm-device-ppp.c
@@ -1,13 +1,5 @@
-/* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
@@ -88,7 +80,7 @@ ppp_ifindex_set (NMPPPManager *ppp_manager,
 	}
 
 	if (old_name)
-		nm_manager_remove_device (nm_manager_get (), old_name, NM_DEVICE_TYPE_PPP);
+		nm_manager_remove_device (NM_MANAGER_GET, old_name, NM_DEVICE_TYPE_PPP);
 
 	nm_device_activate_schedule_stage3_ip_config_start (device);
 }
diff --git a/src/devices/nm-device-ppp.h b/src/devices/nm-device-ppp.h
index 097f4b97..5a169b37 100644
--- a/src/devices/nm-device-ppp.h
+++ b/src/devices/nm-device-ppp.h
@@ -1,13 +1,5 @@
-/* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 83feb130..e87733ef 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2008 Novell, Inc.
  * Copyright (C) 2007 - 2011 Red Hat, Inc.
  */
@@ -26,6 +12,12 @@
 /* This file should only be used by subclasses of NMDevice */
 
 typedef enum {
+	NM_DEVICE_STAGE_STATE_INIT      = 0,
+	NM_DEVICE_STAGE_STATE_PENDING   = 1,
+	NM_DEVICE_STAGE_STATE_COMPLETED = 2,
+} NMDeviceStageState;
+
+typedef enum {
 	NM_DEVICE_IP_STATE_NONE,
 	NM_DEVICE_IP_STATE_WAIT,
 	NM_DEVICE_IP_STATE_CONF,
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index afe83f50..77b46a59 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 - 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 - 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -351,18 +337,17 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceTun *self = NM_DEVICE_TUN (device);
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
-	NMActStageReturn ret;
 
-	ret = NM_DEVICE_CLASS (nm_device_tun_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	/* Nothing to do for TUN devices */
-	if (priv->props.type == IFF_TUN)
-		return NM_ACT_STAGE_RETURN_SUCCESS;
-
-	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
-		return NM_ACT_STAGE_RETURN_FAILURE;
+	if (priv->props.type == IFF_TUN) {
+		/* Nothing to do for TUN devices */
+	} else {
+		if (!nm_device_hw_addr_set_cloned (device,
+		                                   nm_device_get_applied_connection (device),
+		                                   FALSE)) {
+			*out_failure_reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+			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 f665b942..2f87d3bc 100644
--- a/src/devices/nm-device-tun.h
+++ b/src/devices/nm-device-tun.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_TUN_H__
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index 0d6425d4..5c26a036 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/devices/nm-device-veth.h b/src/devices/nm-device-veth.h
index 07217ff9..15da6ab8 100644
--- a/src/devices/nm-device-veth.h
+++ b/src/devices/nm-device-veth.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_VETH_H__
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index f8f6a597..b6efeb81 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -1,20 +1,6 @@
-/* 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 2011 - 2012 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 - 2012 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -477,14 +463,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDevice *parent_device;
 	NMSettingVlan *s_vlan;
-	NMActStageReturn ret;
-
-	ret = NM_DEVICE_CLASS (nm_device_vlan_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
-		return NM_ACT_STAGE_RETURN_FAILURE;
 
 	/* Change MAC address to parent's one if needed */
 	parent_device = nm_device_parent_get_device (device);
@@ -497,7 +475,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	if (s_vlan) {
 		gs_free NMVlanQosMapping *ingress_map = NULL;
 		gs_free NMVlanQosMapping *egress_map = NULL;
-		guint n_ingress_map = 0, n_egress_map = 0;
+		guint n_ingress_map = 0;
+		guint n_egress_map = 0;
 
 		_nm_setting_vlan_get_priorities (s_vlan,
 		                                 NM_VLAN_INGRESS_MAP,
@@ -520,7 +499,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		                              n_egress_map);
 	}
 
-	return ret;
+	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 /*****************************************************************************/
@@ -584,6 +563,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	device_class->link_changed = link_changed;
 	device_class->unrealize_notify = unrealize_notify;
 	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->get_configured_mtu = nm_device_get_configured_mtu_wired_parent;
 	device_class->is_available = is_available;
diff --git a/src/devices/nm-device-vlan.h b/src/devices/nm-device-vlan.h
index c463db58..a5d50a4a 100644
--- a/src/devices/nm-device-vlan.h
+++ b/src/devices/nm-device-vlan.h
@@ -1,20 +1,6 @@
-/* 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 2012 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_VLAN_H__
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index 1a700ba4..29be6854 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 - 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 - 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -481,21 +467,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 }
 
-static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
-{
-	NMActStageReturn ret;
-
-	ret = NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	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;
-}
-
 /*****************************************************************************/
 
 static void
@@ -620,7 +591,7 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 	device_class->complete_connection = complete_connection;
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->update_connection = update_connection;
-	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
 	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
 
 	obj_properties[PROP_ID] =
diff --git a/src/devices/nm-device-vxlan.h b/src/devices/nm-device-vxlan.h
index dd14d910..a36bdf49 100644
--- a/src/devices/nm-device-vxlan.h
+++ b/src/devices/nm-device-vxlan.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013, 2014 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013, 2014 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_VXLAN_H__
diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c
index 5d640b62..c916fa46 100644
--- a/src/devices/nm-device-wireguard.c
+++ b/src/devices/nm-device-wireguard.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * 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 2018 Javier Arteaga <jarteaga@jbeta.is>
+ * Copyright (C) 2018 Javier Arteaga <jarteaga@jbeta.is>
  */
 
 #include "nm-default.h"
@@ -1276,8 +1262,10 @@ _secrets_cb (NMActRequest *req,
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-	} else
-		nm_device_activate_schedule_stage1_device_prepare (device);
+		return;
+	}
+
+	nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 static void
@@ -1518,17 +1506,6 @@ link_config_delayed_resolver_cb (gpointer user_data)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
-{
-	NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (device);
-
-	priv->auto_default_route_initialized = FALSE;
-	priv->auto_default_route_priority_initialized = FALSE;
-
-	return NM_DEVICE_CLASS (nm_device_wireguard_parent_class)->act_stage1_prepare (device, out_failure_reason);
-}
-
-static NMActStageReturn
 act_stage2_config (NMDevice *device,
                    NMDeviceStateReason *out_failure_reason)
 {
@@ -1810,23 +1787,27 @@ reapply_connection (NMDevice *device,
 	NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self);
 	gs_unref_object NMIPConfig *ip4_config = NULL;
 	gs_unref_object NMIPConfig *ip6_config = NULL;
-
-	priv->auto_default_route_refresh = TRUE;
-
-	ip4_config = _get_dev2_ip_config (self, AF_INET);
-	ip6_config = _get_dev2_ip_config (self, AF_INET6);
-
-	nm_device_set_dev2_ip_config (device, AF_INET, ip4_config);
-	nm_device_set_dev2_ip_config (device, AF_INET6, ip6_config);
+	NMDeviceState state = nm_device_get_state (device);
 
 	NM_DEVICE_CLASS (nm_device_wireguard_parent_class)->reapply_connection (device,
 	                                                                        con_old,
 	                                                                        con_new);
 
-	link_config (NM_DEVICE_WIREGUARD (device),
-	             "reapply",
-	             LINK_CONFIG_MODE_REAPPLY,
-	             NULL);
+	if (state >= NM_DEVICE_STATE_CONFIG) {
+		priv->auto_default_route_refresh = TRUE;
+		link_config (NM_DEVICE_WIREGUARD (device),
+		             "reapply",
+		             LINK_CONFIG_MODE_REAPPLY,
+		             NULL);
+	}
+
+	if (state >= NM_DEVICE_STATE_IP_CONFIG) {
+		ip4_config = _get_dev2_ip_config (self, AF_INET);
+		ip6_config = _get_dev2_ip_config (self, AF_INET6);
+
+		nm_device_set_dev2_ip_config (device, AF_INET, ip4_config);
+		nm_device_set_dev2_ip_config (device, AF_INET6, ip6_config);
+	}
 }
 
 /*****************************************************************************/
@@ -1968,7 +1949,6 @@ nm_device_wireguard_class_init (NMDeviceWireGuardClass *klass)
 	device_class->connection_type_check_compatible = NM_SETTING_WIREGUARD_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIREGUARD);
 
-	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->state_changed = device_state_changed;
 	device_class->create_and_realize = create_and_realize;
 	device_class->act_stage2_config = act_stage2_config;
diff --git a/src/devices/nm-device-wireguard.h b/src/devices/nm-device-wireguard.h
index 3ad41f9b..67318042 100644
--- a/src/devices/nm-device-wireguard.h
+++ b/src/devices/nm-device-wireguard.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * 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 2018 Javier Arteaga <jarteaga@jbeta.is>
+ * Copyright (C) 2018 Javier Arteaga <jarteaga@jbeta.is>
  */
 
 #ifndef __NM_DEVICE_WIREGUARD_H__
diff --git a/src/devices/nm-device-wpan.c b/src/devices/nm-device-wpan.c
index 88234412..b947aba6 100644
--- a/src/devices/nm-device-wpan.c
+++ b/src/devices/nm-device-wpan.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
  */
 
 #include "nm-default.h"
@@ -131,12 +117,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	NMDevice *lowpan_device = NULL;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
-	ret = NM_DEVICE_CLASS (nm_device_wpan_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	platform = nm_device_get_platform (device);
-	g_return_val_if_fail (platform, NM_ACT_STAGE_RETURN_FAILURE);
+	nm_assert (NM_IS_PLATFORM (platform));
 
 	ifindex = nm_device_get_ifindex (device);
 
@@ -147,7 +129,11 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	g_return_val_if_fail (s_wpan, NM_ACT_STAGE_RETURN_FAILURE);
 
 	hwaddr = nm_platform_link_get_address (platform, ifindex, &hwaddr_len);
-	g_return_val_if_fail (hwaddr, NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (!hwaddr) {
+		*out_failure_reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
 	/* As of kernel 4.16, the 6LoWPAN devices layered on top of WPANs
 	 * need to be DOWN as well as the WPAN device itself in order to
@@ -156,8 +142,9 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	                                                NM_LINK_TYPE_6LOWPAN,
 	                                                hwaddr,
 	                                                hwaddr_len);
-	if (lowpan_plink && NM_FLAGS_HAS (lowpan_plink->n_ifi_flags, IFF_UP)) {
-		lowpan_device = nm_manager_get_device_by_ifindex (nm_manager_get (),
+	if (   lowpan_plink
+	    && NM_FLAGS_HAS (lowpan_plink->n_ifi_flags, IFF_UP)) {
+		lowpan_device = nm_manager_get_device_by_ifindex (NM_MANAGER_GET,
 		                                                  lowpan_plink->ifindex);
 	}
 
@@ -192,6 +179,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	}
 
 	ret = NM_ACT_STAGE_RETURN_SUCCESS;
+
 out:
 	nm_device_bring_up (device, TRUE, NULL);
 
diff --git a/src/devices/nm-device-wpan.h b/src/devices/nm-device-wpan.h
index 33f24776..d86a9614 100644
--- a/src/devices/nm-device-wpan.h
+++ b/src/devices/nm-device-wpan.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_WPAN_H__
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index b3d97af7..e7a4a059 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -64,6 +50,7 @@
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
 #include "nm-setting-ethtool.h"
+#include "nm-setting-user.h"
 #include "nm-auth-utils.h"
 #include "nm-keep-alive.h"
 #include "nm-netns.h"
@@ -102,11 +89,6 @@ _LOG_DECLARE_SELF (NMDevice);
 
 typedef void (*ActivationHandleFunc) (NMDevice *self);
 
-typedef struct {
-	ActivationHandleFunc func;
-	guint id;
-} ActivationHandleData;
-
 typedef enum {
 	CLEANUP_TYPE_KEEP,
 	CLEANUP_TYPE_REMOVED,
@@ -243,6 +225,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
 	PROP_RX_BYTES,
 	PROP_IP4_CONNECTIVITY,
 	PROP_IP6_CONNECTIVITY,
+	PROP_INTERFACE_FLAGS,
 );
 
 typedef struct _NMDevicePrivate {
@@ -333,8 +316,23 @@ typedef struct _NMDevicePrivate {
 	NMActRequest *  queued_act_request;
 	bool            queued_act_request_is_waiting_for_carrier:1;
 	NMDBusTrackObjPath act_request;
-	ActivationHandleData act_handle4; /* for layer2 and IPv4. */
-	ActivationHandleData act_handle6;
+
+	union {
+		struct {
+			guint activation_source_id_6;
+			guint activation_source_id_4; /* for layer2 and IPv4. */
+		};
+		guint activation_source_id_x[2];
+	};
+
+	union {
+		struct {
+			ActivationHandleFunc activation_source_func_6;
+			ActivationHandleFunc activation_source_func_4; /* for layer2 and IPv4. */
+		};
+		ActivationHandleFunc activation_source_func_x[2];
+	};
+
 	guint           recheck_assume_id;
 
 	struct {
@@ -394,7 +392,6 @@ typedef struct _NMDevicePrivate {
 	NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:5;
 
 	bool            is_enslaved:1;
-	bool            master_ready_handled:1;
 
 	bool            ipv6ll_handle:1; /* TRUE if NM handles the device's IPv6LL address */
 	bool            ipv6ll_has:1;
@@ -403,6 +400,8 @@ typedef struct _NMDevicePrivate {
 
 	bool            concheck_rp_filter_checked:1;
 
+	NMDeviceStageState stage1_sriov_state:3;
+
 	/* Generic DHCP stuff */
 	char *          dhcp_anycast_address;
 
@@ -489,7 +488,7 @@ typedef struct _NMDevicePrivate {
 	/* Firewall */
 	FirewallState fw_state:4;
 	NMFirewallManager *fw_mgr;
-	NMFirewallManagerCallId fw_call;
+	NMFirewallManagerCallId *fw_call;
 
 	/* IPv4LL stuff */
 	sd_ipv4ll *    ipv4ll;
@@ -585,6 +584,7 @@ typedef struct _NMDevicePrivate {
 	} concheck_x[2];
 
 	guint check_delete_unrealized_id;
+	guint32 interface_flags;
 
 	struct {
 		SriovOp *pending;    /* SR-IOV operation currently running */
@@ -633,7 +633,6 @@ static void _carrier_wait_check_queued_act_request (NMDevice *self);
 static gint64 _get_carrier_wait_ms (NMDevice *self);
 
 static const char *_activation_func_to_string (ActivationHandleFunc func);
-static void activation_source_handle_cb (NMDevice *self, int addr_family);
 
 static void _set_state_full (NMDevice *self,
                              NMDeviceState state,
@@ -671,6 +670,10 @@ static void (*const activate_stage4_ip_config_timeout_x[2]) (NMDevice *self) = {
 	activate_stage4_ip_config_timeout_4,
 };
 
+static void sriov_op_cb (GError *error, gpointer user_data);
+
+static void activate_stage2_device_config (NMDevice *self);
+
 static void activate_stage5_ip_config_result_4 (NMDevice *self);
 static void activate_stage5_ip_config_result_6 (NMDevice *self);
 
@@ -1347,6 +1350,7 @@ _get_stable_id (NMDevice *self,
 		       NM_PRINT_FMT_QUOTED (stable_type == NM_UTILS_STABLE_TYPE_GENERATED, " from \"", generated, "\"", ""));
 	}
 
+	nm_assert (priv->current_stable_id);
 	*out_stable_type = priv->current_stable_id_type;
 	return priv->current_stable_id;
 }
@@ -1716,7 +1720,7 @@ _parent_set_ifindex (NMDevice *self,
 	}
 
 	if (parent_ifindex > 0) {
-		parent_device = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+		parent_device = nm_manager_get_device_by_ifindex (NM_MANAGER_GET, parent_ifindex);
 		if (parent_device == self)
 			parent_device = NULL;
 	} else
@@ -2209,7 +2213,7 @@ nm_device_get_route_metric (NMDevice *self,
 	if (route_metric >= 0)
 		goto out;
 
-	route_metric = nm_manager_device_route_metric_reserve (nm_manager_get (),
+	route_metric = nm_manager_device_route_metric_reserve (NM_MANAGER_GET,
 	                                                       nm_device_get_ip_ifindex (self),
 	                                                       nm_device_get_device_type (self));
 out:
@@ -3021,18 +3025,18 @@ concheck_cb (NMConnectivity *connectivity,
 
 	self_keep_alive = g_object_ref (self);
 
-	_LOGT (LOGD_CONCHECK, "connectivity: [Ipv%c] complete check (seq:%llu, state:%s)",
-	       nm_utils_addr_family_to_char (handle->addr_family),
-	       (long long unsigned) handle->seq,
-	       nm_connectivity_state_to_string (state));
-
 	/* keep @self alive, while we invoke callbacks. */
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	nm_assert (!handle || c_list_contains (&priv->concheck_lst_head, &handle->concheck_lst));
+	nm_assert (handle && c_list_contains (&priv->concheck_lst_head, &handle->concheck_lst));
 
 	seq = handle->seq;
 
+	_LOGT (LOGD_CONCHECK, "connectivity: [Ipv%c] complete check (seq:%llu, state:%s)",
+	       nm_utils_addr_family_to_char (handle->addr_family),
+	       (long long unsigned) handle->seq,
+	       nm_connectivity_state_to_string (state));
+
 	/* find out, if there are any periodic checks pending (either whether they
 	 * were scheduled before or after @handle. */
 	any_periodic_before = FALSE;
@@ -3600,12 +3604,26 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceState state = nm_device_get_state (self);
+	gboolean notify_flags = FALSE;
 
 	if (priv->carrier == carrier)
 		return;
 
+	if (NM_FLAGS_ALL (priv->capabilities,
+	                    NM_DEVICE_CAP_CARRIER_DETECT
+	                  | NM_DEVICE_CAP_NONSTANDARD_CARRIER)) {
+		if (carrier)
+			priv->interface_flags |= NM_DEVICE_INTERFACE_FLAG_CARRIER;
+		else
+			priv->interface_flags &= ~NM_DEVICE_INTERFACE_FLAG_CARRIER;
+		notify_flags = TRUE;
+	}
+
 	priv->carrier = carrier;
-	_notify (self, PROP_CARRIER);
+	if (notify_flags)
+		nm_gobject_notify_together (self, PROP_CARRIER, PROP_INTERFACE_FLAGS);
+	else
+		_notify (self, PROP_CARRIER);
 
 	if (priv->carrier) {
 		_LOGI (LOGD_DEVICE, "carrier: link connected");
@@ -3675,7 +3693,7 @@ device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink)
 	if (plink->master <= 0)
 		return;
 
-	master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master);
+	master = nm_manager_get_device_by_ifindex (NM_MANAGER_GET, plink->master);
 	plink_master = nm_platform_link_get (nm_device_get_platform (self), plink->master);
 	plink_master_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink_master));
 
@@ -3802,6 +3820,33 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
 	g_array_unref (dns_domains);
 }
 
+static void
+device_update_interface_flags (NMDevice *self, const NMPlatformLink *plink)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceInterfaceFlags flags = NM_DEVICE_INTERFACE_FLAG_NONE;
+
+	if (plink && NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP))
+		flags |= NM_DEVICE_INTERFACE_FLAG_UP;
+	if (plink && NM_FLAGS_HAS (plink->n_ifi_flags, IFF_LOWER_UP))
+		flags |= NM_DEVICE_INTERFACE_FLAG_LOWER_UP;
+
+	if (NM_FLAGS_ALL (priv->capabilities,
+	                    NM_DEVICE_CAP_CARRIER_DETECT
+	                  | NM_DEVICE_CAP_NONSTANDARD_CARRIER)) {
+		if (priv->carrier)
+			flags |= NM_DEVICE_INTERFACE_FLAG_CARRIER;
+	} else {
+		if (plink && NM_FLAGS_HAS (plink->n_ifi_flags, IFF_LOWER_UP))
+			flags |= NM_DEVICE_INTERFACE_FLAG_CARRIER;
+	}
+
+	if (flags != priv->interface_flags) {
+		priv->interface_flags = flags;
+		_notify (self, PROP_INTERFACE_FLAGS);
+	}
+}
+
 static gboolean
 device_link_changed (NMDevice *self)
 {
@@ -3889,6 +3934,8 @@ device_link_changed (NMDevice *self)
 	    && !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
 		nm_device_set_carrier (self, pllink->connected);
 
+	device_update_interface_flags (self, pllink);
+
 	klass->link_changed (self, pllink);
 
 	/* Update DHCP, etc, if needed */
@@ -4241,9 +4288,11 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink
 		_notify (self, PROP_IFINDEX);
 		NM_DEVICE_GET_CLASS (self)->link_changed (self, plink);
 	}
+
+	device_update_interface_flags (self, plink);
 }
 
-static void sriov_op_cb (GError *error, gpointer user_data);
+/*****************************************************************************/
 
 static void
 sriov_op_start (NMDevice *self, SriovOp *op)
@@ -4276,11 +4325,14 @@ sriov_op_cb (GError *error, gpointer user_data)
 
 	priv->sriov.pending = NULL;
 
+	g_clear_object (&op->cancellable);
+
 	if (op->callback)
 		op->callback (error, op->callback_data);
 
-	g_clear_object (&op->cancellable);
-	g_slice_free (SriovOp, op);
+	nm_assert (!priv->sriov.pending);
+
+	nm_g_slice_free (op);
 
 	if (priv->sriov.next) {
 		sriov_op_start (self,
@@ -4289,41 +4341,82 @@ sriov_op_cb (GError *error, gpointer user_data)
 }
 
 static void
-sriov_op_queue (NMDevice *self,
-                guint num_vfs,
-                NMTernary autoprobe,
-                NMPlatformAsyncCallback callback,
-                gpointer callback_data)
+sriov_op_queue_op (NMDevice *self,
+                   SriovOp *op)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	GError *error = NULL;
-	SriovOp *op;
-
-	op = g_slice_new0 (SriovOp);
-	op->num_vfs = num_vfs;
-	op->autoprobe = autoprobe;
-	op->callback = callback;
-	op->callback_data = callback_data;
 
 	if (priv->sriov.next) {
+		SriovOp *op_next = g_steal_pointer (&priv->sriov.next);
+
 		/* Cancel the next operation immediately */
-		if (priv->sriov.next->callback) {
+		if (op_next->callback) {
+			gs_free_error GError *error = NULL;
+
 			nm_utils_error_set_cancelled (&error, FALSE, NULL);
-			priv->sriov.next->callback (error, priv->sriov.next->callback_data);
-			g_clear_error (&error);
+			op_next->callback (error, op_next->callback_data);
 		}
-		g_slice_free (SriovOp, priv->sriov.next);
-		priv->sriov.next = NULL;
-	}
 
-	if (priv->sriov.pending) {
+		nm_g_slice_free (op_next);
+
+		if (!priv->sriov.pending) {
+			/* This (having "next" set but "pending" not) can only happen if we are
+			 * called from inside the callback again.
+			 *
+			 * That means we append the new request as "next" and return. Once
+			 * the callback returns, it will schedule the request. */
+			priv->sriov.next = op;
+			return;
+		}
+	} else if (priv->sriov.pending) {
 		priv->sriov.next = op;
 		g_cancellable_cancel (priv->sriov.pending->cancellable);
-	} else
+		return;
+	}
+
+	if (op)
 		sriov_op_start (self, op);
 }
 
 static void
+sriov_op_queue (NMDevice *self,
+                guint num_vfs,
+                NMTernary autoprobe,
+                NMPlatformAsyncCallback callback,
+                gpointer callback_data)
+{
+	SriovOp *op;
+
+	/* We usually never want to cancel an async write operation, unless it's superseded
+	 * by a newer operation (that resets the state). That is, because we need to ensure
+	 * that we never end up doing two concurrent writes (since we write on a background
+	 * thread, that would be unordered/racy).
+	 * Of course, since we queue requests only per-device, when devices get renamed we
+	 * might end up writing the same sysctl concurrently still. But that's really
+	 * unlikely, and don't rename after udev completes!
+	 *
+	 * The "next" operation is not yet even started. It can be replaced/canceled right away
+	 * when a newer request comes.
+	 * The "pending" operation is currently ongoing, and we may cancel it if
+	 * we have a follow-up operation (queued in "next"). Unless we have a such
+	 * a newer request, we cannot cancel it!
+	 *
+	 * FIXME(shutdown): However, during shutdown we don't have a follow-up write request to cancel
+	 * this operation and we have to give it at least some time to complete. The solution is that
+	 * we register a way to abort the last call during shutdown, and after NM_SHUTDOWN_TIMEOUT_MS
+	 * grace period we pull the plug and cancel it. */
+
+	op = g_slice_new (SriovOp);
+	*op = (SriovOp) {
+		.num_vfs       = num_vfs,
+		.autoprobe     = autoprobe,
+		.callback      = callback,
+		.callback_data = callback_data,
+	};
+	sriov_op_queue_op (self, op);
+}
+
+static void
 device_init_static_sriov_num_vfs (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -4746,42 +4839,24 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 	return TRUE;
 }
 
-/**
- * nm_device_notify_component_added():
- * @self: the #NMDevice
- * @component: the component being added by a plugin
- *
- * Called by the manager to notify the device that a new component has
- * been found.  The device implementation should return %TRUE if it
- * wishes to claim the component, or %FALSE if it cannot.
- *
- * Returns: %TRUE to claim the component, %FALSE if the component cannot be
- * claimed.
- */
-gboolean
-nm_device_notify_component_added (NMDevice *self, GObject *component)
+void
+nm_device_notify_availability_maybe_changed (NMDevice *self)
 {
-	NMDeviceClass *klass;
 	NMDevicePrivate *priv;
 
-	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	klass = NM_DEVICE_GET_CLASS (self);
-
-	if (priv->state == NM_DEVICE_STATE_DISCONNECTED) {
-		/* A device could have stayed disconnected because it would
-		 * want to register with a network server that now become
-		 * available. */
-		nm_device_recheck_available_connections (self);
-		if (g_hash_table_size (priv->available_connections) > 0)
-			nm_device_emit_recheck_auto_activate (self);
-	}
 
-	if (klass->component_added)
-		return klass->component_added (self, component);
+	if (priv->state != NM_DEVICE_STATE_DISCONNECTED)
+		return;
 
-	return FALSE;
+	/* A device could have stayed disconnected because it would
+	 * want to register with a network server that now become
+	 * available. */
+	nm_device_recheck_available_connections (self);
+	if (g_hash_table_size (priv->available_connections) > 0)
+		nm_device_emit_recheck_auto_activate (self);
 }
 
 /**
@@ -5856,9 +5931,10 @@ check_connection_compatible (NMDevice *self, NMConnection *connection, GError **
 		return FALSE;
 	}
 
-	conn_iface = nm_manager_get_connection_iface (nm_manager_get (),
+	conn_iface = nm_manager_get_connection_iface (NM_MANAGER_GET,
 	                                              connection,
 	                                              NULL,
+	                                              NULL,
 	                                              &local);
 
 	/* We always need a interface name for virtual devices, but for
@@ -6103,119 +6179,102 @@ dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer us
 
 /*****************************************************************************/
 
-static gboolean
-activation_source_handle_cb4 (gpointer user_data)
-{
-	activation_source_handle_cb (user_data, AF_INET);
-	return G_SOURCE_REMOVE;
-}
-
-static gboolean
-activation_source_handle_cb6 (gpointer user_data)
-{
-	activation_source_handle_cb (user_data, AF_INET6);
-	return G_SOURCE_REMOVE;
-}
-
-static ActivationHandleData *
-activation_source_get_by_family (NMDevice *self,
-                                 int addr_family,
-                                 GSourceFunc *out_idle_func)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	switch (addr_family) {
-	case AF_INET6:
-		NM_SET_OUT (out_idle_func, activation_source_handle_cb6);
-		return &priv->act_handle6;
-	case AF_INET:
-		NM_SET_OUT (out_idle_func, activation_source_handle_cb4);
-		return &priv->act_handle4;
-	}
-	g_return_val_if_reached (NULL);
-}
-
 static void
 activation_source_clear (NMDevice *self,
                          int addr_family)
 {
-	ActivationHandleData *act_data;
-
-	act_data = activation_source_get_by_family (self, addr_family, NULL);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const gboolean IS_IPv4 = (addr_family == AF_INET);
 
-	if (act_data->id) {
+	if (priv->activation_source_id_x[IS_IPv4] != 0) {
 		_LOGD (LOGD_DEVICE, "activation-stage: clear %s,v%c (id %u)",
-		       _activation_func_to_string (act_data->func),
+		       _activation_func_to_string (priv->activation_source_func_x[IS_IPv4]),
 		       nm_utils_addr_family_to_char (addr_family),
-		       act_data->id);
-		nm_clear_g_source (&act_data->id);
-		act_data->func = NULL;
+		       priv->activation_source_id_x[IS_IPv4]);
+		nm_clear_g_source (&priv->activation_source_id_x[IS_IPv4]);
+		priv->activation_source_func_x[IS_IPv4] = NULL;
 	}
 }
 
-static void
+static gboolean
 activation_source_handle_cb (NMDevice *self,
                              int addr_family)
 {
-	ActivationHandleData *act_data, a;
+	NMDevicePrivate *priv;
+	const gboolean IS_IPv4 = (addr_family == AF_INET);
+	ActivationHandleFunc activation_source_func;
+	guint activation_source_id;
 
-	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
 
-	act_data = activation_source_get_by_family (self, addr_family, NULL);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	g_return_if_fail (act_data->id);
-	g_return_if_fail (act_data->func);
+	activation_source_func = priv->activation_source_func_x[IS_IPv4];
+	activation_source_id = priv->activation_source_id_x[IS_IPv4];
 
-	a = *act_data;
+	g_return_val_if_fail (activation_source_id != 0, G_SOURCE_REMOVE);
+	nm_assert (activation_source_func);
 
-	act_data->func = NULL;
-	act_data->id = 0;
+	priv->activation_source_func_x[IS_IPv4] = NULL;
+	priv->activation_source_id_x[IS_IPv4] = 0;
 
 	_LOGD (LOGD_DEVICE, "activation-stage: invoke %s,v%c (id %u)",
-	       _activation_func_to_string (a.func),
+	       _activation_func_to_string (activation_source_func),
 	       nm_utils_addr_family_to_char (addr_family),
-	       a.id);
+	       activation_source_id);
 
-	a.func (self);
+	activation_source_func (self);
 
-	_LOGD (LOGD_DEVICE, "activation-stage: complete %s,v%c (id %u)",
-	       _activation_func_to_string (a.func),
+	_LOGT (LOGD_DEVICE, "activation-stage: complete %s,v%c (id %u)",
+	       _activation_func_to_string (activation_source_func),
 	       nm_utils_addr_family_to_char (addr_family),
-	       a.id);
+	       activation_source_id);
+
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+activation_source_handle_cb_4 (gpointer user_data)
+{
+	return activation_source_handle_cb (user_data, AF_INET);
+}
+
+static gboolean
+activation_source_handle_cb_6 (gpointer user_data)
+{
+	return activation_source_handle_cb (user_data, AF_INET6);
 }
 
 static void
 activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int addr_family)
 {
-	ActivationHandleData *act_data;
-	GSourceFunc source_func = NULL;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const gboolean IS_IPv4 = (addr_family == AF_INET);
 	guint new_id = 0;
 
-	act_data = activation_source_get_by_family (self, addr_family, &source_func);
-
-	if (act_data->id && act_data->func == func) {
-		/* Don't bother rescheduling the same function that's about to
-		 * run anyway.  Fixes issues with crappy wireless drivers sending
-		 * streams of associate events before NM has had a chance to process
-		 * the first one.
-		 */
-		_LOGD (LOGD_DEVICE, "activation-stage: already scheduled %s,v%c (id %u)",
+	if (   priv->activation_source_id_x[IS_IPv4] != 0
+	    && priv->activation_source_func_x[IS_IPv4] == func) {
+		/* Scheduling the same stage multiple times is fine. */
+		_LOGT (LOGD_DEVICE, "activation-stage: already scheduled %s,v%c (id %u)",
 		       _activation_func_to_string (func),
 		       nm_utils_addr_family_to_char (addr_family),
-		       act_data->id);
+		       priv->activation_source_id_x[IS_IPv4]);
 		return;
 	}
 
-	new_id = g_idle_add (source_func, self);
+	new_id = g_idle_add (  IS_IPv4
+	                     ? activation_source_handle_cb_4
+	                     : activation_source_handle_cb_6,
+	                     self);
 
-	if (act_data->id) {
-		_LOGW (LOGD_DEVICE, "activation-stage: schedule %s,v%c which replaces %s,v%c (id %u -> %u)",
+	if (priv->activation_source_id_x[IS_IPv4] != 0) {
+		_LOGD (LOGD_DEVICE, "activation-stage: schedule %s,v%c which replaces %s,v%c (id %u -> %u)",
 		       _activation_func_to_string (func),
 		       nm_utils_addr_family_to_char (addr_family),
-		       _activation_func_to_string (act_data->func),
+		       _activation_func_to_string (priv->activation_source_func_x[IS_IPv4]),
 		       nm_utils_addr_family_to_char (addr_family),
-		       act_data->id, new_id);
-		nm_clear_g_source (&act_data->id);
+		       priv->activation_source_id_x[IS_IPv4], new_id);
+		nm_clear_g_source (&priv->activation_source_id_x[IS_IPv4]);
 	} else {
 		_LOGD (LOGD_DEVICE, "activation-stage: schedule %s,v%c (id %u)",
 		       _activation_func_to_string (func),
@@ -6223,19 +6282,38 @@ activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int addr_
 		       new_id);
 	}
 
-	act_data->func = func;
-	act_data->id = new_id;
+	priv->activation_source_func_x[IS_IPv4] = func;
+	priv->activation_source_id_x[IS_IPv4] = new_id;
 }
 
-static gboolean
-activation_source_is_scheduled (NMDevice *self,
-                                ActivationHandleFunc func,
-                                int addr_family)
+static void
+activation_source_invoke_sync (NMDevice *self, ActivationHandleFunc func, int addr_family)
 {
-	ActivationHandleData *act_data;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const gboolean IS_IPv4 = (addr_family == AF_INET);
 
-	act_data = activation_source_get_by_family (self, addr_family, NULL);
-	return act_data->func == func;
+	if (priv->activation_source_id_x[IS_IPv4] == 0) {
+		_LOGD (LOGD_DEVICE, "activation-stage: synchronously invoke %s,v%c",
+		       _activation_func_to_string (func),
+		       nm_utils_addr_family_to_char (addr_family));
+	} else if (priv->activation_source_func_x[IS_IPv4] == func) {
+		_LOGD (LOGD_DEVICE, "activation-stage: synchronously invoke %s,v%c which was already scheduled (id %u)",
+		       _activation_func_to_string (func),
+		       nm_utils_addr_family_to_char (addr_family),
+		       priv->activation_source_id_x[IS_IPv4]);
+	} else {
+		_LOGD (LOGD_DEVICE, "activation-stage: synchronously invoke %s,v%c which replaces %s,v%c (id %u)",
+		       _activation_func_to_string (func),
+		       nm_utils_addr_family_to_char (addr_family),
+		       _activation_func_to_string (priv->activation_source_func_x[IS_IPv4]),
+		       nm_utils_addr_family_to_char (addr_family),
+		       priv->activation_source_id_x[IS_IPv4]);
+	}
+
+	nm_clear_g_source (&priv->activation_source_id_x[IS_IPv4]);
+	priv->activation_source_func_x[IS_IPv4] = NULL;
+
+	func (self);
 }
 
 /*****************************************************************************/
@@ -6248,22 +6326,18 @@ master_ready (NMDevice *self,
 	NMActiveConnection *master_connection;
 	NMDevice *master;
 
-	g_return_if_fail (priv->state == NM_DEVICE_STATE_PREPARE);
-	g_return_if_fail (!priv->master_ready_handled);
-
 	/* Notify a master device that it has a new slave */
-	g_return_if_fail (nm_active_connection_get_master_ready (active));
-	master_connection = nm_active_connection_get_master (active);
+	nm_assert (nm_active_connection_get_master_ready (active));
 
-	priv->master_ready_handled = TRUE;
-	nm_clear_g_signal_handler (active, &priv->master_ready_id);
+	master_connection = nm_active_connection_get_master (active);
 
 	master = nm_active_connection_get_device (master_connection);
 
 	_LOGD (LOGD_DEVICE, "master connection ready; master device %s",
 	       nm_device_get_iface (master));
 
-	if (priv->master && priv->master != master)
+	if (   priv->master
+	    && priv->master != master)
 		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
 	/* If the master didn't change, add-slave only rechecks whether to assume a connection. */
@@ -6277,8 +6351,12 @@ master_ready_cb (NMActiveConnection *active,
                  GParamSpec *pspec,
                  NMDevice *self)
 {
-	master_ready (self, active);
-	nm_device_activate_schedule_stage2_device_config (self);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	nm_assert (nm_active_connection_get_master_ready (active));
+
+	if (priv->state == NM_DEVICE_STATE_PREPARE)
+		nm_device_activate_schedule_stage1_device_prepare (self);
 }
 
 static void
@@ -6424,64 +6502,9 @@ sriov_params_cb (GError *error, gpointer data)
 		return;
 	}
 
-	nm_device_activate_schedule_stage2_device_config (self);
-}
-
-static NMActStageReturn
-act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMSettingSriov *s_sriov;
-	guint i, num;
+	priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_COMPLETED;
 
-	if (   priv->ifindex > 0
-	    && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV)
-	    && (s_sriov = nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) {
-		nm_auto_freev NMPlatformVF **plat_vfs = NULL;
-		gs_free_error GError *error = NULL;
-		NMSriovVF *vf;
-		NMTernary autoprobe;
-		gpointer *data;
-
-		autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov);
-		if (autoprobe == NM_TERNARY_DEFAULT) {
-			autoprobe = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
-			                                                         NM_CON_DEFAULT ("sriov.autoprobe-drivers"),
-			                                                         self,
-			                                                         NM_TERNARY_FALSE,
-			                                                         NM_TERNARY_TRUE,
-			                                                         NM_TERNARY_TRUE);
-		}
-
-		num = nm_setting_sriov_get_num_vfs (s_sriov);
-		plat_vfs = g_new0 (NMPlatformVF *, num + 1);
-		for (i = 0; i < num; i++) {
-			vf = nm_setting_sriov_get_vf (s_sriov, i);
-			plat_vfs[i] = sriov_vf_config_to_platform (self, vf, &error);
-			if (!plat_vfs[i]) {
-				_LOGE (LOGD_DEVICE,
-				       "failed to apply SR-IOV VF '%s': %s",
-				       nm_utils_sriov_vf_to_str (vf, FALSE, NULL),
-				       error->message);
-				NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
-				return NM_ACT_STAGE_RETURN_FAILURE;
-			}
-		}
-
-		/* When changing the number of VFs the kernel can block
-		 * for very long time in the write to sysfs, especially
-		 * if autoprobe-drivers is enabled. Do it asynchronously
-		 * to avoid blocking the entire NM process.
-		 */
-		data = nm_utils_user_data_pack (self, g_steal_pointer (&plat_vfs));
-		sriov_op_queue (self,
-		                nm_setting_sriov_get_total_vfs (s_sriov),
-		                autoprobe,
-		                sriov_params_cb,
-		                data);
-		return NM_ACT_STAGE_RETURN_POSTPONE;
-	}
-	return NM_ACT_STAGE_RETURN_SUCCESS;
+	nm_device_activate_schedule_stage1_device_prepare (self);
 }
 
 /*
@@ -6495,6 +6518,8 @@ activate_stage1_device_prepare (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	NMActiveConnection *active;
+	NMActiveConnection *master;
 
 	priv->v4_route_table_initialized = FALSE;
 	priv->v6_route_table_initialized = FALSE;
@@ -6509,21 +6534,117 @@ activate_stage1_device_prepare (NMDevice *self)
 
 	nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
 
+	if (priv->stage1_sriov_state != NM_DEVICE_STAGE_STATE_COMPLETED) {
+		NMSettingSriov *s_sriov;
+
+		if (nm_device_sys_iface_state_is_external_or_assume (self)) {
+			/* pass */
+		} else if (priv->stage1_sriov_state == NM_DEVICE_STAGE_STATE_PENDING)
+			return;
+		else if (   priv->ifindex > 0
+		         && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV)
+		         && (s_sriov = nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) {
+			nm_auto_freev NMPlatformVF **plat_vfs = NULL;
+			gs_free_error GError *error = NULL;
+			NMSriovVF *vf;
+			NMTernary autoprobe;
+			guint i, num;
+
+			autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov);
+			if (autoprobe == NM_TERNARY_DEFAULT) {
+				autoprobe = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+				                                                         NM_CON_DEFAULT ("sriov.autoprobe-drivers"),
+				                                                         self,
+				                                                         NM_TERNARY_FALSE,
+				                                                         NM_TERNARY_TRUE,
+				                                                         NM_TERNARY_TRUE);
+			}
+
+			num = nm_setting_sriov_get_num_vfs (s_sriov);
+			plat_vfs = g_new0 (NMPlatformVF *, num + 1);
+			for (i = 0; i < num; i++) {
+				vf = nm_setting_sriov_get_vf (s_sriov, i);
+				plat_vfs[i] = sriov_vf_config_to_platform (self, vf, &error);
+				if (!plat_vfs[i]) {
+					_LOGE (LOGD_DEVICE,
+					       "failed to apply SR-IOV VF '%s': %s",
+					       nm_utils_sriov_vf_to_str (vf, FALSE, NULL),
+					       error->message);
+					nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+					return;
+				}
+			}
+
+			/* When changing the number of VFs the kernel can block
+			 * for very long time in the write to sysfs, especially
+			 * if autoprobe-drivers is enabled. Do it asynchronously
+			 * to avoid blocking the entire NM process.
+			 */
+			sriov_op_queue (self,
+			                nm_setting_sriov_get_total_vfs (s_sriov),
+			                autoprobe,
+			                sriov_params_cb,
+			                nm_utils_user_data_pack (self,
+			                                         g_steal_pointer (&plat_vfs)));
+			priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_PENDING;
+			return;
+		}
+		priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+	}
+
 	/* Assumed connections were already set up outside NetworkManager */
 	if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
-		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
+		NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
+
+		if (klass->act_stage1_prepare_set_hwaddr_ethernet) {
+			if (!nm_device_hw_addr_set_cloned (self,
+			                                   nm_device_get_applied_connection (self),
+			                                   FALSE)) {
+				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+				return;
+			}
+		}
+
+		if (klass->act_stage1_prepare) {
+			NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
+
+			ret = klass->act_stage1_prepare (self, &failure_reason);
+			if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
+				return;
+			}
+			if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
+				return;
+
+			nm_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+		}
+	}
 
-		ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &failure_reason);
-		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+	active = NM_ACTIVE_CONNECTION (priv->act_request.obj);
+	master = nm_active_connection_get_master (active);
+	if (master) {
+		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);
 			return;
-		} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
+		}
+		/* If the master connection is ready for slaves, attach ourselves */
+		if (!nm_active_connection_get_master_ready (active)) {
+			if (priv->master_ready_id == 0) {
+				_LOGD (LOGD_DEVICE, "waiting for master connection to become ready");
+				priv->master_ready_id = g_signal_connect (active,
+				                                          "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY,
+				                                          (GCallback) master_ready_cb,
+				                                          self);
+			}
 			return;
 		}
-		g_return_if_fail (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 	}
+	nm_clear_g_signal_handler (priv->act_request.obj, &priv->master_ready_id);
+	if (master)
+		master_ready (self, active);
 
-	nm_device_activate_schedule_stage2_device_config (self);
+	activation_source_invoke_sync (self, activate_stage2_device_config, AF_INET);
 }
 
 /*
@@ -6914,44 +7035,8 @@ activate_stage2_device_config (NMDevice *self)
 void
 nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 {
-	NMDevicePrivate *priv;
-
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request.obj);
-
-	if (!priv->master_ready_handled) {
-		NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request.obj);
-		NMActiveConnection *master;
-
-		master = 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 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) {
-					priv->master_ready_id = g_signal_connect (active,
-					                                          "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY,
-					                                          (GCallback) master_ready_cb,
-					                                          self);
-				}
-				/* Postpone */
-				return;
-			}
-		}
-	}
-
 	activation_source_schedule (self, activate_stage2_device_config, AF_INET);
 }
 
@@ -7403,8 +7488,6 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 		/* Stop any ongoing DHCP transaction on this device */
 		nm_clear_g_signal_handler (priv->dhcp4.client, &priv->dhcp4.state_sigid);
 
-		nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP4, FALSE);
-
 		if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
 		    || cleanup_type == CLEANUP_TYPE_REMOVED)
 			nm_dhcp_client_stop (priv->dhcp4.client, release);
@@ -7552,7 +7635,7 @@ ip_config_merge_and_apply (NMDevice *self,
 		nm_ip_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0);
 
 	if (priv->ext_ip_config_x[IS_IPv4])
-		nm_ip_config_merge (composite, priv->ext_ip_config_x[IS_IPv4], NM_IP_CONFIG_MERGE_DEFAULT, 0);
+		nm_ip_config_merge (composite, priv->ext_ip_config_x[IS_IPv4], NM_IP_CONFIG_MERGE_EXTERNAL, 0);
 
 	/* Merge WWAN config *last* to ensure modem-given settings overwrite
 	 * any external stuff set by pppd or other scripts.
@@ -7658,8 +7741,6 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 	                           NULL,
 	                           NULL, NULL, NULL);
 
-	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP4, FALSE);
-
 	return TRUE;
 }
 
@@ -7688,7 +7769,13 @@ dhcp4_fail (NMDevice *self, NMDhcpState dhcp_state)
 	       _ip_state_to_string (priv->ip_state_4),
 	       priv->dhcp4.was_active);
 
-	/* Keep client running if there are static addresses configured
+	/* The client is always left running after a failure. */
+
+	/* Nothing to do if we failed before... */
+	if (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL)
+		goto clear_config;
+
+	/* ... and also if there are static addresses configured
 	 * on the interface.
 	 */
 	if (   priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE
@@ -7704,14 +7791,12 @@ dhcp4_fail (NMDevice *self, NMDhcpState dhcp_state)
 	 */
 	if (   dhcp_state == NM_DHCP_STATE_TERMINATED
 	    || (!priv->dhcp4.was_active && priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF)) {
-		dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 		nm_device_activate_schedule_ip_config_timeout (self, AF_INET);
 		return;
 	}
 
 	/* In any other case (expired lease, assumed connection, etc.),
-	 * start a grace period in which we keep the client running,
-	 * hoping that it will regain a lease.
+	 * wait for some time before failing the IP method.
 	 */
 	if (!priv->dhcp4.grace_id) {
 		priv->dhcp4.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC,
@@ -7869,6 +7954,191 @@ get_dhcp_timeout (NMDevice *self, int addr_family)
 	return timeout ?: NM_DHCP_TIMEOUT_DEFAULT;
 }
 
+/**
+ * dhcp_get_iaid:
+ * @self: the #NMDevice
+ * @addr_family: the address family
+ * @connection: the connection
+ * @out_is_explicit: on return, %TRUE if the user set a valid IAID in
+ *   the connection or in global configuration; %FALSE if the connection
+ *   property was empty and no valid global configuration was provided.
+ *
+ * Returns: a IAID value for this device and the given connection.
+ */
+static guint32
+dhcp_get_iaid (NMDevice *self,
+               int addr_family,
+               NMConnection *connection,
+               gboolean *out_is_explicit)
+{
+	NMSettingIPConfig *s_ip;
+	const char *iaid_str;
+	gs_free char *iaid_str_free = NULL;
+	guint32 iaid;
+	const char *iface;
+	const char *fail_reason;
+	gboolean is_explicit = TRUE;
+
+	s_ip = nm_connection_get_setting_ip_config (connection, addr_family);
+	iaid_str = nm_setting_ip_config_get_dhcp_iaid (s_ip);
+	if (!iaid_str) {
+		iaid_str_free = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+		                                                         addr_family == AF_INET
+		                                                       ? NM_CON_DEFAULT ("ipv4.dhcp-iaid")
+		                                                       : NM_CON_DEFAULT ("ipv6.dhcp-iaid"),
+		                                                       self);
+		iaid_str = iaid_str_free;
+		if (!iaid_str) {
+			iaid_str = NM_IAID_IFNAME;
+			is_explicit = FALSE;
+		} else if (!_nm_utils_iaid_verify (iaid_str, NULL)) {
+			_LOGW (LOGD_DEVICE, "invalid global default '%s' for ipv%c.dhcp-iaid",
+			       iaid_str,
+			       nm_utils_addr_family_to_char (addr_family));
+			iaid_str = NM_IAID_IFNAME;
+			is_explicit = FALSE;
+		}
+	}
+
+	if (nm_streq0 (iaid_str, NM_IAID_MAC)) {
+		const NMPlatformLink *pllink;
+
+		pllink = nm_platform_link_get (nm_device_get_platform (self),
+		                               nm_device_get_ip_ifindex (self));
+		if (!pllink || pllink->l_address.len < 4) {
+			fail_reason = "invalid link-layer address";
+			goto out_fail;
+		}
+
+		/* @iaid is in native endianness. Use unaligned_read_be32()
+		 * so that the IAID for a given MAC address is the same on
+		 * BE and LE machines. */
+		iaid = unaligned_read_be32 (&pllink->l_address.data[pllink->l_address.len - 4]);
+		goto out_good;
+	} else if (nm_streq0 (iaid_str, NM_IAID_PERM_MAC)) {
+		guint8 hwaddr_buf[NM_UTILS_HWADDR_LEN_MAX];
+		const char *hwaddr_str;
+		gsize hwaddr_len;
+
+		hwaddr_str = nm_device_get_permanent_hw_address (self);
+		if (!hwaddr_str) {
+			fail_reason = "no permanent link-layer address";
+			goto out_fail;
+		}
+
+		if (!_nm_utils_hwaddr_aton (hwaddr_str, hwaddr_buf, sizeof (hwaddr_buf), &hwaddr_len))
+			g_return_val_if_reached (0);
+
+		if (hwaddr_len < 4) {
+			fail_reason = "invalid link-layer address";
+			goto out_fail;
+		}
+
+		iaid = unaligned_read_be32 (&hwaddr_buf[hwaddr_len - 4]);
+		goto out_good;
+	}  else if (nm_streq (iaid_str, "stable")) {
+		nm_auto_free_checksum GChecksum *sum = NULL;
+		guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA1];
+		NMUtilsStableType stable_type;
+		const char *stable_id;
+		guint32 salted_header;
+		const guint8 *host_id;
+		gsize host_id_len;
+
+		stable_id = _get_stable_id (self, connection, &stable_type);
+		salted_header = htonl (53390459 + stable_type);
+		nm_utils_host_id_get (&host_id, &host_id_len);
+		iface = nm_device_get_ip_iface (self);
+
+		sum = g_checksum_new (G_CHECKSUM_SHA1);
+		g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header));
+		g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1);
+		g_checksum_update (sum, (const guchar *) iface, strlen (iface) + 1);
+		g_checksum_update (sum, (const guchar *) host_id, host_id_len);
+		nm_utils_checksum_get_digest (sum, digest);
+
+		iaid = unaligned_read_be32 (digest);
+		goto out_good;
+	} else if ((iaid = _nm_utils_ascii_str_to_int64 (iaid_str, 10, 0, G_MAXUINT32, -1)) != -1) {
+		goto out_good;
+	} else {
+		iface = nm_device_get_ip_iface (self);
+		iaid = nm_utils_create_dhcp_iaid (TRUE,
+		                                  (const guint8 *) iface,
+		                                  strlen (iface));
+		goto out_good;
+	}
+
+out_fail:
+	nm_assert (fail_reason);
+	_LOGW (  addr_family == AF_INET
+	       ? (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4)
+	       : (LOGD_DEVICE | LOGD_DHCP6 | LOGD_IP6),
+	       "ipv%c.dhcp-iaid: failure to generate IAID: %s. Using interface-name based IAID",
+	       nm_utils_addr_family_to_char (addr_family), fail_reason);
+	is_explicit = FALSE;
+	iface = nm_device_get_ip_iface (self);
+	iaid = nm_utils_create_dhcp_iaid (TRUE,
+	                                  (const guint8 *) iface,
+	                                  strlen (iface));
+out_good:
+	_LOGD (  addr_family == AF_INET
+	       ? (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4)
+	       : (LOGD_DEVICE | LOGD_DHCP6 | LOGD_IP6),
+	       "ipv%c.dhcp-iaid: using %u (0x%08x) IAID (str: '%s', explicit %d)",
+	       nm_utils_addr_family_to_char (addr_family), iaid, iaid,
+	       iaid_str, is_explicit);
+	NM_SET_OUT (out_is_explicit, is_explicit);
+	return iaid;
+}
+
+static NMDhcpHostnameFlags
+get_dhcp_hostname_flags (NMDevice *self, int addr_family)
+{
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip;
+	NMDhcpHostnameFlags flags;
+	gs_free_error GError *error = NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_DHCP_HOSTNAME_FLAG_NONE);
+
+	connection = nm_device_get_applied_connection (self);
+	s_ip = nm_connection_get_setting_ip_config (connection, addr_family);
+	g_return_val_if_fail (s_ip, NM_DHCP_HOSTNAME_FLAG_NONE);
+
+	if (!nm_setting_ip_config_get_dhcp_send_hostname (s_ip))
+		return NM_DHCP_HOSTNAME_FLAG_NONE;
+
+	flags = nm_setting_ip_config_get_dhcp_hostname_flags (s_ip);
+	if (flags != NM_DHCP_HOSTNAME_FLAG_NONE)
+		return flags;
+
+	flags = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                     addr_family == AF_INET
+	                                                       ? NM_CON_DEFAULT ("ipv4.dhcp-hostname-flags")
+	                                                       : NM_CON_DEFAULT ("ipv6.dhcp-hostname-flags"),
+	                                                     self,
+	                                                     0, NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS,
+	                                                     0);
+
+	if (!_nm_utils_validate_dhcp_hostname_flags (flags, addr_family, &error)) {
+		_LOGW (LOGD_DEVICE, "invalid global default value 0x%x for ipv%d.%s: %s",
+		       (guint) flags,
+		       addr_family == AF_INET ? 4 : 6,
+		       NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS,
+		       error->message);
+		flags = NM_DHCP_HOSTNAME_FLAG_NONE;
+	}
+
+	if (flags != NM_DHCP_HOSTNAME_FLAG_NONE)
+		return flags;
+
+	if (addr_family == AF_INET)
+		return NM_DHCP_HOSTNAME_FLAGS_FQDN_DEFAULT_IP4;
+	else
+		return NM_DHCP_HOSTNAME_FLAGS_FQDN_DEFAULT_IP6;
+}
+
 static GBytes *
 dhcp4_get_client_id (NMDevice *self,
                      NMConnection *connection,
@@ -7945,8 +8215,9 @@ dhcp4_get_client_id (NMDevice *self,
 	}
 
 	if (nm_streq (client_id, "duid")) {
-		result = nm_utils_dhcp_client_id_systemd_node_specific (TRUE,
-		                                                        nm_device_get_ip_iface (self));
+		guint32 iaid = dhcp_get_iaid (self, AF_INET, connection, NULL);
+
+		result = nm_utils_dhcp_client_id_systemd_node_specific (iaid);
 		goto out_good;
 	}
 
@@ -7960,11 +8231,7 @@ dhcp4_get_client_id (NMDevice *self,
 		gsize host_id_len;
 
 		stable_id = _get_stable_id (self, connection, &stable_type);
-		if (!stable_id)
-			g_return_val_if_reached (NULL);
-
 		salted_header = htonl (2011610591 + stable_type);
-
 		nm_utils_host_id_get (&host_id, &host_id_len);
 
 		sum = g_checksum_new (G_CHECKSUM_SHA1);
@@ -8044,12 +8311,12 @@ dhcp4_start (NMDevice *self)
 	                                                nm_setting_ip_config_get_dhcp_send_hostname (s_ip4),
 	                                                nm_setting_ip_config_get_dhcp_hostname (s_ip4),
 	                                                nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4)),
+	                                                get_dhcp_hostname_flags (self, AF_INET),
 	                                                client_id,
 	                                                get_dhcp_timeout (self, AF_INET),
 	                                                priv->dhcp_anycast_address,
 	                                                NULL,
 	                                                &error);
-
 	if (!priv->dhcp4.client) {
 		_LOGW (LOGD_DHCP4, "failure to start DHCP: %s", error->message);
 		g_clear_error (&error);
@@ -8061,8 +8328,6 @@ dhcp4_start (NMDevice *self)
 	                                            G_CALLBACK (dhcp4_state_changed),
 	                                            self);
 
-	nm_device_add_pending_action (self, NM_PENDING_ACTION_DHCP4, TRUE);
-
 	if (nm_device_sys_iface_state_is_external_or_assume (self))
 		priv->dhcp4.was_active = TRUE;
 
@@ -8267,8 +8532,6 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 		g_clear_object (&priv->dhcp6.client);
 	}
 
-	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP6, FALSE);
-
 	if (priv->dhcp6.config) {
 		nm_dbus_object_clear_and_unexport (&priv->dhcp6.config);
 		_notify (self, PROP_DHCP6_CONFIG);
@@ -8302,8 +8565,6 @@ dhcp6_lease_change (NMDevice *self)
 	                           NULL,
 	                           NULL, NULL, NULL);
 
-	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP6, FALSE);
-
 	return TRUE;
 }
 
@@ -8333,10 +8594,16 @@ dhcp6_fail (NMDevice *self, NMDhcpState dhcp_state)
 	       _ip_state_to_string (priv->ip_state_6),
 	       priv->dhcp6.was_active);
 
+	/* The client is always left running after a failure. */
+
+	/* Nothing to do if we failed before... */
+	if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL)
+		goto clear_config;
+
 	is_dhcp_managed = (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED);
 
 	if (is_dhcp_managed) {
-		/* Keep client running if there are static addresses configured
+		/* ... and also if there are static addresses configured
 		 * on the interface.
 		 */
 		if (   priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE
@@ -8352,14 +8619,12 @@ dhcp6_fail (NMDevice *self, NMDhcpState dhcp_state)
 		 */
 		if (   dhcp_state == NM_DHCP_STATE_TERMINATED
 		    || (!priv->dhcp6.was_active && priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF)) {
-			dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 			nm_device_activate_schedule_ip_config_timeout (self, AF_INET6);
 			return;
 		}
 
 		/* In any other case (expired lease, assumed connection, etc.),
-		 * start a grace period in which we keep the client running,
-		 * hoping that it will regain a lease.
+		 * wait for some time before failing the IP method.
 		 */
 		if (!priv->dhcp6.grace_id) {
 			priv->dhcp6.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC,
@@ -8690,8 +8955,6 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole
 		} digest;
 
 		stable_id = _get_stable_id (self, connection, &stable_type);
-		if (!stable_id)
-			g_return_val_if_reached (NULL);
 
 		if (NM_IN_STRSET (duid, "stable-ll", "stable-llt")) {
 			/* for stable LL/LLT DUIDs, we still need a hardware address to detect
@@ -8820,6 +9083,8 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	gboolean enforce_duid = FALSE;
 	const NMPlatformLink *pllink;
 	GError *error = NULL;
+	guint32 iaid;
+	gboolean iaid_explicit;
 
 	const NMPlatformIP6Address *ll_addr = NULL;
 
@@ -8844,6 +9109,8 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 		bcast_hwaddr = nmp_link_address_get_as_bytes (&pllink->l_broadcast);
 	}
 
+	iaid = dhcp_get_iaid (self, AF_INET6, connection, &iaid_explicit);
+
 	duid = dhcp6_get_duid (self, connection, hwaddr, &enforce_duid);
 	priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                nm_device_get_multi_index (self),
@@ -8857,8 +9124,11 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	                                                nm_device_get_route_metric (self, AF_INET6),
 	                                                nm_setting_ip_config_get_dhcp_send_hostname (s_ip6),
 	                                                nm_setting_ip_config_get_dhcp_hostname (s_ip6),
+	                                                get_dhcp_hostname_flags (self, AF_INET6),
 	                                                duid,
 	                                                enforce_duid,
+	                                                iaid,
+	                                                iaid_explicit,
 	                                                get_dhcp_timeout (self, AF_INET6),
 	                                                priv->dhcp_anycast_address,
 	                                                (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
@@ -8893,7 +9163,6 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
-	NMSettingIPConfig *s_ip6;
 
 	nm_dbus_object_clear_and_unexport (&priv->dhcp6.config);
 	priv->dhcp6.config = nm_dhcp6_config_new ();
@@ -8903,11 +9172,7 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll)
 	g_clear_pointer (&priv->dhcp6.event_id, g_free);
 
 	connection = nm_device_get_applied_connection (self);
-	g_assert (connection);
-	s_ip6 = nm_connection_get_setting_ip6_config (connection);
-	if (!nm_setting_ip_config_get_may_fail (s_ip6) ||
-	    !strcmp (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_DHCP))
-		nm_device_add_pending_action (self, NM_PENDING_ACTION_DHCP6, TRUE);
+	g_return_val_if_fail (connection, FALSE);
 
 	if (wait_for_ll) {
 		/* ensure link local is ready... */
@@ -9134,13 +9399,12 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 		const char *stable_id;
 
 		stable_id = _get_stable_id (self, 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)) {
+		if (!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);
@@ -9794,7 +10058,6 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	g_assert (s_ip6);
 
 	stable_id = _get_stable_id (self, connection, &stable_type);
-	g_assert (stable_id);
 	priv->ndisc = nm_lndp_ndisc_new (nm_device_get_platform (self),
 	                                 nm_device_get_ip_ifindex (self),
 	                                 nm_device_get_ip_iface (self),
@@ -9817,9 +10080,6 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 		                 "IPv6 private addresses. This feature is not available");
 	}
 
-	if (!nm_setting_ip_config_get_may_fail (nm_connection_get_setting_ip6_config (connection)))
-		nm_device_add_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, TRUE);
-
 	/* ensure link local is ready... */
 	if (!linklocal6_start (self)) {
 		/* wait for the LL address to show up */
@@ -9840,8 +10100,6 @@ addrconf6_cleanup (NMDevice *self)
 	nm_clear_g_signal_handler (priv->ndisc, &priv->ndisc_changed_id);
 	nm_clear_g_signal_handler (priv->ndisc, &priv->ndisc_timeout_id);
 
-	nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE);
-
 	applied_config_clear (&priv->ac_ip6_config);
 	g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref);
 	nm_clear_g_source (&priv->rt6_temporary_not_available_id);
@@ -10002,7 +10260,7 @@ _ip6_privacy_get (NMDevice *self)
 		return ip6_privacy;
 
 	if (!nm_device_get_ip_ifindex (self))
-		return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;;
+		return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
 
 	/* 3.) No valid default-value configured. Fallback to reading sysctl.
 	 *
@@ -10376,7 +10634,7 @@ activate_stage3_ip_config_start (NMDevice *self)
 
 static void
 fw_change_zone_cb (NMFirewallManager *firewall_manager,
-                   NMFirewallManagerCallId call_id,
+                   NMFirewallManagerCallId *call_id,
                    GError *error,
                    gpointer user_data)
 {
@@ -10389,6 +10647,7 @@ fw_change_zone_cb (NMFirewallManager *firewall_manager,
 
 	if (priv->fw_call != call_id)
 		g_return_if_reached ();
+
 	priv->fw_call = NULL;
 
 	if (nm_utils_error_is_cancelled (error, FALSE))
@@ -10668,7 +10927,7 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error)
 		 * the announced setting without restarting dnsmasq. That means, if the default
 		 * route changes w.r.t. being metered, then the shared connection does not get
 		 * updated before reactivating. */
-		announce_android_metered = NM_IN_SET (nm_manager_get_metered (nm_manager_get ()),
+		announce_android_metered = NM_IN_SET (nm_manager_get_metered (NM_MANAGER_GET),
 		                                      NM_METERED_YES,
 		                                      NM_METERED_GUESS_YES);
 		break;
@@ -10841,8 +11100,6 @@ activate_stage5_ip_config_result_4 (NMDevice *self)
 	if (do_announce)
 		nm_device_arp_announce (self);
 
-	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP4, FALSE);
-
 	/* Enter the IP_CHECK state if this is the first method to complete */
 	_set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_DONE);
 	check_ip_state (self, FALSE, TRUE);
@@ -11004,8 +11261,6 @@ activate_stage5_ip_config_result_6 (NMDevice *self)
 				return;
 			}
 		}
-		nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP6, FALSE);
-		nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE);
 
 		/* Start IPv6 forwarding if we need it */
 		method = nm_device_get_effective_ip_config_method (self, AF_INET6);
@@ -11360,7 +11615,8 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 			}
 		}
 
-		if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
+		if (   nm_device_get_ip_ifindex (self) > 0
+		    && !ip_config_merge_and_apply (self, AF_INET, TRUE))
 			_LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration");
 	}
 }
@@ -11433,7 +11689,8 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 			}
 		}
 
-		if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+		if (   nm_device_get_ip_ifindex (self) > 0
+		    && !ip_config_merge_and_apply (self, AF_INET6, TRUE))
 			_LOGW (LOGD_IP4, "Failed to reapply IPv6 configuration");
 	}
 }
@@ -11492,10 +11749,19 @@ can_reapply_change (NMDevice *self,
 		                                          NM_SETTING_CONNECTION_MDNS,
 		                                          NM_SETTING_CONNECTION_LLMNR);
 	} else if (NM_IN_STRSET (setting_name,
+	                         NM_SETTING_USER_SETTING_NAME,
 	                         NM_SETTING_PROXY_SETTING_NAME,
 	                         NM_SETTING_IP4_CONFIG_SETTING_NAME,
 	                         NM_SETTING_IP6_CONFIG_SETTING_NAME)) {
 		return TRUE;
+	} else if (   nm_streq (setting_name, NM_SETTING_WIRED_SETTING_NAME)
+	           && NM_IN_SET (NM_DEVICE_GET_CLASS (self)->get_configured_mtu,
+	                         nm_device_get_configured_mtu_wired_parent,
+	                         nm_device_get_configured_mtu_for_wired)) {
+		return nm_device_hash_check_invalid_keys (diffs,
+		                                          NM_SETTING_WIRED_SETTING_NAME,
+		                                          error,
+		                                          NM_SETTING_WIRED_MTU);
 	} else {
 		g_set_error (error,
 		             NM_DEVICE_ERROR,
@@ -11541,7 +11807,8 @@ check_and_reapply_connection (NMDevice *self,
 	NMSettingIPConfig *s_ip6_old, *s_ip6_new;
 	GHashTableIter iter;
 
-	if (priv->state != NM_DEVICE_STATE_ACTIVATED) {
+	if (   priv->state < NM_DEVICE_STATE_PREPARE
+	    || priv->state > NM_DEVICE_STATE_ACTIVATED) {
 		g_set_error_literal (error,
 		                     NM_DEVICE_ERROR,
 		                     NM_DEVICE_ERROR_NOT_ACTIVE,
@@ -11654,24 +11921,31 @@ check_and_reapply_connection (NMDevice *self,
 	 *************************************************************************/
 	klass->reapply_connection (self, con_old, con_new);
 
-	nm_device_update_firewall_zone (self);
-	nm_device_update_metered (self);
-	lldp_init (self, FALSE);
+	if (priv->state >= NM_DEVICE_STATE_CONFIG)
+		lldp_init (self, FALSE);
 
-	s_ip4_old = nm_connection_get_setting_ip4_config (con_old);
-	s_ip4_new = nm_connection_get_setting_ip4_config (con_new);
-	s_ip6_old = nm_connection_get_setting_ip6_config (con_old);
-	s_ip6_new = nm_connection_get_setting_ip6_config (con_new);
+	if (priv->state >= NM_DEVICE_STATE_IP_CONFIG) {
+		s_ip4_old = nm_connection_get_setting_ip4_config (con_old);
+		s_ip4_new = nm_connection_get_setting_ip4_config (con_new);
+		s_ip6_old = nm_connection_get_setting_ip6_config (con_old);
+		s_ip6_new = nm_connection_get_setting_ip6_config (con_new);
 
-	/* Allow reapply of MTU */
-	priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE;
+		/* Allow reapply of MTU */
+		priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE;
 
-	nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new);
-	nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new);
+		nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new);
+		nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new);
 
-	_routing_rules_sync (self, NM_TERNARY_TRUE);
+		_routing_rules_sync (self, NM_TERNARY_TRUE);
 
-	reactivate_proxy_config (self);
+		reactivate_proxy_config (self);
+	}
+
+	if (priv->state >= NM_DEVICE_STATE_IP_CHECK)
+		nm_device_update_firewall_zone (self);
+
+	if (priv->state >= NM_DEVICE_STATE_ACTIVATED)
+		nm_device_update_metered (self);
 
 	return TRUE;
 }
@@ -11768,7 +12042,8 @@ impl_device_reapply (NMDBusObject *obj,
 		return;
 	}
 
-	if (priv->state != NM_DEVICE_STATE_ACTIVATED) {
+	if (   priv->state < NM_DEVICE_STATE_PREPARE
+	    || priv->state > NM_DEVICE_STATE_ACTIVATED) {
 		error = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_NOT_ACTIVE,
 		                             "Device is not activated");
@@ -12353,7 +12628,7 @@ nm_device_is_activating (NMDevice *self)
 	 * handler is actually run.  If there's an activation handler scheduled
 	 * we're activating anyway.
 	 */
-	return priv->act_handle4.id ? TRUE : FALSE;
+	return priv->activation_source_id_4 != 0;
 }
 
 NMProxyConfig *
@@ -13335,9 +13610,8 @@ queued_ip_config_change (NMDevice *self, int addr_family)
 	 * it changing IP configurations before they are applied. Postpone the
 	 * update in such case.
 	 */
-	if (activation_source_is_scheduled (self,
-	                                    activate_stage5_ip_config_result_x[IS_IPv4],
-	                                    addr_family))
+	if (   priv->activation_source_id_x[IS_IPv4] != 0
+	    && priv->activation_source_func_x[IS_IPv4] == activate_stage5_ip_config_result_x[IS_IPv4])
 		return G_SOURCE_CONTINUE;
 
 	priv->queued_ip_config_id_x[IS_IPv4] = 0;
@@ -14115,6 +14389,11 @@ nm_device_update_metered (NMDevice *self)
 		}
 	}
 
+	if (   value == NM_METERED_INVALID
+	    && NM_DEVICE_GET_CLASS (self)->get_guessed_metered
+	    && NM_DEVICE_GET_CLASS (self)->get_guessed_metered (self))
+		value = NM_METERED_GUESS_YES;
+
 	/* Try to guess a value using the metered flag in IP configuration */
 	if (value == NM_METERED_INVALID) {
 		if (   priv->ip_config_4
@@ -14676,8 +14955,10 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
 
 	_cancel_activation (self);
 
+	priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_INIT;
+
 	if (cleanup_type != CLEANUP_TYPE_KEEP) {
-		nm_manager_device_route_metric_clear (nm_manager_get (),
+		nm_manager_device_route_metric_clear (NM_MANAGER_GET,
 		                                      nm_device_get_ip_ifindex (self));
 	}
 
@@ -14754,10 +15035,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 
 	if (priv->act_request.obj) {
 		nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request.obj), AF_INET, FALSE);
-
-		priv->master_ready_handled = FALSE;
 		nm_clear_g_signal_handler (priv->act_request.obj, &priv->master_ready_id);
-
 		act_request_set (self, NULL);
 	}
 
@@ -14949,7 +15227,7 @@ nm_device_spawn_iface_helper (NMDevice *self)
 	g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
 
 	stable_id = _get_stable_id (self, connection, &stable_type);
-	if (stable_id && stable_type != NM_UTILS_STABLE_TYPE_UUID) {
+	if (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));
 	}
@@ -15112,9 +15390,9 @@ deactivate_ready (NMDevice *self, NMDeviceStateReason reason)
 	if (priv->dispatcher.call_id)
 		return;
 
-	if (priv->sriov.pending)
+	if (   priv->sriov.pending
+	    || priv->sriov.next)
 		return;
-	nm_assert (!priv->sriov.next);
 
 	nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason);
 }
@@ -15208,6 +15486,13 @@ _set_state_full (NMDevice *self,
 
 	old_state = priv->state;
 
+	if (   state == NM_DEVICE_STATE_FAILED
+	    && nm_device_sys_iface_state_is_external_or_assume (self)) {
+		/* Avoid tearing down assumed connection, assume it's connected */
+		state = NM_DEVICE_STATE_ACTIVATED;
+		reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
+	}
+
 	/* Do nothing if state isn't changing, but as a special case allow
 	 * re-setting UNAVAILABLE if the device is missing firmware so that we
 	 * can retry device initialization.
@@ -15459,14 +15744,6 @@ _set_state_full (NMDevice *self,
 		 */
 		_cancel_activation (self);
 
-		if (nm_device_sys_iface_state_is_external_or_assume (self)) {
-			/* Avoid tearing down assumed connection, assume it's connected */
-			nm_device_queue_state (self,
-			                       NM_DEVICE_STATE_ACTIVATED,
-			                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
-			break;
-		}
-
 		sett_conn = nm_device_get_settings_connection (self);
 		_LOGW (LOGD_DEVICE | LOGD_WIFI,
 		       "Activation: failed for connection '%s'",
@@ -16237,12 +16514,10 @@ _hw_addr_get_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi,
 		}
 
 		stable_id = _get_stable_id (self, 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));
-		}
+		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) {
 			g_set_error (error,
 			             NM_DEVICE_ERROR,
@@ -16698,14 +16973,15 @@ dispose (GObject *object)
 
 	_cleanup_generic_pre (self, CLEANUP_TYPE_KEEP);
 
-	g_warn_if_fail (c_list_is_empty (&priv->slaves));
-	g_assert (priv->master_ready_id == 0);
+	nm_assert (c_list_is_empty (&priv->slaves));
 
 	/* Let the kernel manage IPv6LL again */
 	set_nm_ipv6ll (self, FALSE);
 
 	_cleanup_generic_post (self, CLEANUP_TYPE_KEEP);
 
+	nm_assert (priv->master_ready_id == 0);
+
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
 	nm_clear_g_source (&priv->recheck_assume_id);
@@ -17093,6 +17369,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_IP6_CONNECTIVITY:
 		g_value_set_uint (value, priv->concheck_x[0].state);
 		break;
+	case PROP_INTERFACE_FLAGS:
+		g_value_set_uint (value, priv->interface_flags);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -17181,6 +17460,7 @@ static const NMDBusInterfaceInfoExtended interface_info_device = {
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Real",                 "b",      NM_DEVICE_REAL),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE       ("Ip4Connectivity",      "u",      NM_DEVICE_IP4_CONNECTIVITY),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE       ("Ip6Connectivity",      "u",      NM_DEVICE_IP6_CONNECTIVITY),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE       ("InterfaceFlags",       "u",      NM_DEVICE_INTERFACE_FLAGS),
 		),
 	),
 };
@@ -17221,7 +17501,6 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->link_changed = link_changed;
 
 	klass->is_available = is_available;
-	klass->act_stage1_prepare = act_stage1_prepare;
 	klass->act_stage2_config = act_stage2_config;
 	klass->act_stage3_ip_config_start = act_stage3_ip_config_start;
 	klass->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout;
@@ -17409,14 +17688,6 @@ nm_device_class_init (NMDeviceClass *klass)
 	                          FALSE,
 	                          G_PARAM_READABLE |
 	                          G_PARAM_STATIC_STRINGS);
-
-	/**
-	 * NMDevice:metered:
-	 *
-	 * Whether the connection is metered.
-	 *
-	 * Since: 1.2
-	 **/
 	obj_properties[PROP_METERED] =
 	    g_param_spec_uint (NM_DEVICE_METERED, "", "",
 	                       0, G_MAXUINT32, NM_METERED_UNKNOWN,
@@ -17465,6 +17736,13 @@ nm_device_class_init (NMDeviceClass *klass)
 	                        NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
 	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_INTERFACE_FLAGS] =
+	    g_param_spec_uint (NM_DEVICE_INTERFACE_FLAGS, "", "",
+	                       0,
+	                       G_MAXUINT32,
+	                       0,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 412a57a0..518c66ca 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -67,9 +53,6 @@ nm_device_state_reason_check (NMDeviceStateReason reason)
 }
 
 #define NM_PENDING_ACTION_AUTOACTIVATE              "autoactivate"
-#define NM_PENDING_ACTION_DHCP4                     "dhcp4"
-#define NM_PENDING_ACTION_DHCP6                     "dhcp6"
-#define NM_PENDING_ACTION_AUTOCONF6                 "autoconf6"
 #define NM_PENDING_ACTION_RECHECK_AVAILABLE         "recheck-available"
 #define NM_PENDING_ACTION_CARRIER_WAIT              "carrier-wait"
 #define NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT    "waiting-for-supplicant"
@@ -149,6 +132,7 @@ nm_device_state_reason_check (NMDeviceStateReason reason)
 
 #define NM_DEVICE_IP4_CONNECTIVITY           "ip4-connectivity"
 #define NM_DEVICE_IP6_CONNECTIVITY           "ip6-connectivity"
+#define NM_DEVICE_INTERFACE_FLAGS            "interface-flags"
 
 #define NM_TYPE_DEVICE            (nm_device_get_type ())
 #define NM_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice))
@@ -435,23 +419,6 @@ typedef struct _NMDeviceClass {
 	                                           int new_ifindex,
 	                                           NMDevice *new_parent);
 
-	/**
-	 * component_added:
-	 * @self: the #NMDevice
-	 * @component: the component (device, modem, etc) which was added
-	 *
-	 * Notifies @self that a new component that a device might be interested
-	 * in was detected by some device factory. It may include an object of
-	 * %GObject subclass to help the devices decide whether it claims that
-	 * particular object itself and the emitting factory should not.
-	 *
-	 * Returns: %TRUE if the component was claimed exclusively and no further
-	 * devices should be notified of the new component.  %FALSE to indicate
-	 * that the component was not exclusively claimed and other devices should
-	 * be notified.
-	 */
-	gboolean        (* component_added) (NMDevice *self, GObject *component);
-
 	gboolean        (* owns_iface) (NMDevice *self, const char *iface);
 
 	NMConnection *  (* new_default_connection) (NMDevice *self);
@@ -472,10 +439,15 @@ typedef struct _NMDeviceClass {
 	guint32         (* get_dhcp_timeout) (NMDevice *self,
 	                                      int addr_family);
 
+	gboolean        (* get_guessed_metered) (NMDevice *self);
+
 	/* Controls, whether to call act_stage2_config() callback also for assuming
 	 * a device or for external activations. In this case, act_stage2_config() must
 	 * take care not to touch the device's configuration. */
 	bool act_stage2_config_also_for_external_or_assume:1;
+
+	bool act_stage1_prepare_set_hwaddr_ethernet:1;
+
 } NMDeviceClass;
 
 typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device,
@@ -825,7 +797,7 @@ gboolean   nm_device_check_connection_available (NMDevice *device,
                                                  const char *specific_object,
                                                  GError **error);
 
-gboolean nm_device_notify_component_added (NMDevice *device, GObject *component);
+void nm_device_notify_availability_maybe_changed (NMDevice *self);
 
 gboolean nm_device_owns_iface (NMDevice *device, const char *iface);
 
@@ -885,12 +857,22 @@ void nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle);
 NMConnectivityState nm_device_get_connectivity_state (NMDevice *self, int addr_family);
 
 typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer;
+
+typedef void (*NMBtVTableRegisterCallback) (GError *error,
+                                            gpointer user_data);
+
 struct _NMBtVTableNetworkServer {
 	gboolean (*is_available) (const NMBtVTableNetworkServer *vtable,
-	                          const char *addr);
+	                          const char *addr,
+	                          NMDevice *device_accept_busy);
+
 	gboolean (*register_bridge) (const NMBtVTableNetworkServer *vtable,
 	                             const char *addr,
-	                             NMDevice *device);
+	                             NMDevice *device,
+	                             GCancellable *cancellable,
+	                             NMBtVTableRegisterCallback callback,
+	                             gpointer callback_user_data,
+	                             GError **error);
 	gboolean (*unregister_bridge) (const NMBtVTableNetworkServer *vtable,
 	                               NMDevice *device);
 };
diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c
index 469ccce1..4c9e7705 100644
--- a/src/devices/nm-lldp-listener.c
+++ b/src/devices/nm-lldp-listener.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/devices/nm-lldp-listener.h b/src/devices/nm-lldp-listener.h
index c44ca59f..92e1d00f 100644
--- a/src/devices/nm-lldp-listener.h
+++ b/src/devices/nm-lldp-listener.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/devices/ovs/meson.build b/src/devices/ovs/meson.build
index 834b27b0..27e1b4d0 100644
--- a/src/devices/ovs/meson.build
+++ b/src/devices/ovs/meson.build
@@ -7,14 +7,15 @@ sources = files(
 )
 
 deps = [
+  daemon_nm_default_dep,
   jansson_dep,
-  nm_dep,
 ]
 
 libnm_device_plugin_ovs = shared_module(
   'nm-device-plugin-ovs',
   sources: sources,
   dependencies: deps,
+  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
@@ -28,10 +29,3 @@ test(
   check_exports,
   args: [libnm_device_plugin_ovs.full_path(), linker_script_devices],
 )
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-ovs: src/devices/ovs/libnm-device-plugin-ovs.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so)
-'''
diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c
index be707e7a..59096803 100644
--- a/src/devices/ovs/nm-device-ovs-bridge.c
+++ b/src/devices/ovs/nm-device-ovs-bridge.c
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/devices/ovs/nm-device-ovs-bridge.h b/src/devices/ovs/nm-device-ovs-bridge.h
index 631b4754..07a1fee7 100644
--- a/src/devices/ovs/nm-device-ovs-bridge.h
+++ b/src/devices/ovs/nm-device-ovs-bridge.h
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__
diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c
index 83de3c3d..726e9901 100644
--- a/src/devices/ovs/nm-device-ovs-interface.c
+++ b/src/devices/ovs/nm-device-ovs-interface.c
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -112,7 +98,9 @@ link_changed (NMDevice *device,
 {
 	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device);
 
-	if (pllink && priv->waiting_for_interface) {
+	if (   pllink
+	    && priv->waiting_for_interface
+	    && nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
 		priv->waiting_for_interface = FALSE;
 		nm_device_bring_up (device, TRUE, NULL);
 		nm_device_activate_schedule_stage3_ip_config_start (device);
@@ -156,6 +144,15 @@ can_unmanaged_external_down (NMDevice *self)
 	return FALSE;
 }
 
+static void
+deactivate (NMDevice *device)
+{
+	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
+	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (self);
+
+	priv->waiting_for_interface = FALSE;
+}
+
 /*****************************************************************************/
 
 static void
@@ -185,6 +182,7 @@ nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
 	device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH);
 
+	device_class->deactivate = deactivate;
 	device_class->get_type_description = get_type_description;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
diff --git a/src/devices/ovs/nm-device-ovs-interface.h b/src/devices/ovs/nm-device-ovs-interface.h
index a748e206..e31dac8b 100644
--- a/src/devices/ovs/nm-device-ovs-interface.h
+++ b/src/devices/ovs/nm-device-ovs-interface.h
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__
diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c
index 39e64b3f..0955e8a9 100644
--- a/src/devices/ovs/nm-device-ovs-port.c
+++ b/src/devices/ovs/nm-device-ovs-port.c
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/devices/ovs/nm-device-ovs-port.h b/src/devices/ovs/nm-device-ovs-port.h
index 5ccf1ec1..7edbf02f 100644
--- a/src/devices/ovs/nm-device-ovs-port.h
+++ b/src/devices/ovs/nm-device-ovs-port.h
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_OVS_PORT_H__
diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c
index fdf07bd3..d6dd13eb 100644
--- a/src/devices/ovs/nm-ovs-factory.c
+++ b/src/devices/ovs/nm-ovs-factory.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
@@ -83,7 +69,7 @@ new_device_from_type (const char *name, NMDeviceType device_type)
 	const char *type_desc;
 	NMLinkType link_type = NM_LINK_TYPE_NONE;
 
-	if (nm_manager_get_device (nm_manager_get (), name, device_type))
+	if (nm_manager_get_device (NM_MANAGER_GET, name, device_type))
 		return NULL;
 
 	if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) {
@@ -130,7 +116,7 @@ ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type
 	NMDevice *device;
 	NMDeviceState device_state;
 
-	device = nm_manager_get_device (nm_manager_get (), name, device_type);
+	device = nm_manager_get_device (NM_MANAGER_GET, name, device_type);
 	if (!device)
 		return;
 
@@ -158,7 +144,7 @@ ovsdb_interface_failed (NMOvsdb *ovsdb,
 
 	_LOGI (name, connection_uuid, "ovs interface \"%s\" (%s) failed: %s", name, connection_uuid, error);
 
-	device = nm_manager_get_device (nm_manager_get (), name, NM_DEVICE_TYPE_OVS_INTERFACE);
+	device = nm_manager_get_device (NM_MANAGER_GET, name, NM_DEVICE_TYPE_OVS_INTERFACE);
 	if (!device)
 		return;
 
diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c
index c0e68db3..ec4f5c74 100644
--- a/src/devices/ovs/nm-ovsdb.c
+++ b/src/devices/ovs/nm-ovsdb.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
diff --git a/src/devices/ovs/nm-ovsdb.h b/src/devices/ovs/nm-ovsdb.h
index e7f9d719..59f46206 100644
--- a/src/devices/ovs/nm-ovsdb.h
+++ b/src/devices/ovs/nm-ovsdb.h
@@ -1,20 +1,6 @@
-/* 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 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_OVSDB_H__
diff --git a/src/devices/team/meson.build b/src/devices/team/meson.build
index 3f755012..0e63183b 100644
--- a/src/devices/team/meson.build
+++ b/src/devices/team/meson.build
@@ -4,15 +4,16 @@ sources = files(
 )
 
 deps = [
+  daemon_nm_default_dep,
   jansson_dep,
   libteamdctl_dep,
-  nm_dep,
 ]
 
 libnm_device_plugin_team = shared_module(
   'nm-device-plugin-team',
   sources: sources,
   dependencies: deps,
+  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
@@ -26,10 +27,3 @@ test(
   check_exports,
   args: [libnm_device_plugin_team.full_path(), linker_script_devices],
 )
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-team: src/devices/team/libnm-device-plugin-team.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/team/.libs/libnm-device-plugin-team.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/team/.libs/libnm-device-plugin-team.so)
-'''
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 4661a840..fb9c9c69 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Jiri Pirko <jiri@resnulli.us>
  * Copyright (C) 2018 Red Hat, Inc.
  */
@@ -49,14 +35,14 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceTeam,
 
 typedef struct {
 	struct teamdctl *tdc;
+	char *config;
 	GPid teamd_pid;
 	guint teamd_process_watch;
 	guint teamd_timeout;
 	guint teamd_read_timeout;
 	guint teamd_dbus_watch;
-	char *config;
-	gboolean kill_in_progress;
-	NMConnection *connection;
+	bool kill_in_progress:1;
+	NMDeviceStageState stage1_state:3;
 } NMDeviceTeamPrivate;
 
 struct _NMDeviceTeam {
@@ -70,11 +56,11 @@ struct _NMDeviceTeamClass {
 
 G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE)
 
-#define NM_DEVICE_TEAM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceTeam, NM_IS_DEVICE_TEAM)
+#define NM_DEVICE_TEAM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceTeam, NM_IS_DEVICE_TEAM, NMDevice)
 
 /*****************************************************************************/
 
-static gboolean teamd_start (NMDevice *device, NMConnection *connection);
+static gboolean teamd_start (NMDeviceTeam *self);
 
 /*****************************************************************************/
 
@@ -141,9 +127,8 @@ _get_config (NMDeviceTeam *self)
 }
 
 static gboolean
-teamd_read_config (NMDevice *device)
+teamd_read_config (NMDeviceTeam *self)
 {
-	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	const char *config = NULL;
 	int err;
@@ -170,11 +155,11 @@ teamd_read_config (NMDevice *device)
 static gboolean
 teamd_read_timeout_cb (gpointer user_data)
 {
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE ((NMDeviceTeam *) user_data);
+	NMDeviceTeam *self = user_data;
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
-	teamd_read_config ((NMDevice *) user_data);
 	priv->teamd_read_timeout = 0;
-
+	teamd_read_config (self);
 	return G_SOURCE_REMOVE;
 }
 
@@ -192,8 +177,9 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 
 	/* Read the configuration only if not already set */
-	if (!priv->config && ensure_teamd_connection (device))
-		teamd_read_config (device);
+	if (   !priv->config
+	    && ensure_teamd_connection (device))
+		teamd_read_config (self);
 
 	/* Restore previous tdc state */
 	if (priv->tdc && !tdc) {
@@ -273,31 +259,32 @@ master_update_slave_connection (NMDevice *self,
 }
 
 /*****************************************************************************/
+
 static void
 teamd_kill_cb (pid_t pid, gboolean success, int child_status, void *user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
-	NMDeviceTeam *self = (NMDeviceTeam *) device;
+	gs_unref_object NMDeviceTeam *self = user_data;
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
 	priv->kill_in_progress = FALSE;
 
-	if (priv->connection) {
-		_LOGT (LOGD_TEAM, "kill terminated, starting teamd...");
-		if (!teamd_start (device, priv->connection)) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
-		}
-		g_clear_object (&priv->connection);
+	if (nm_device_get_state (NM_DEVICE (self)) != NM_DEVICE_STATE_PREPARE) {
+		_LOGT (LOGD_TEAM, "kill terminated");
+		return;
+	}
+
+	_LOGT (LOGD_TEAM, "kill terminated, starting teamd...");
+	if (!teamd_start (self)) {
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 	}
-	g_object_unref (device);
 }
 
 static void
-teamd_cleanup (NMDevice *device, gboolean free_tdc)
+teamd_cleanup (NMDeviceTeam *self, gboolean free_tdc)
 {
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE ((NMDeviceTeam *) device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
 	nm_clear_g_source (&priv->teamd_process_watch);
 	nm_clear_g_source (&priv->teamd_timeout);
@@ -305,15 +292,18 @@ teamd_cleanup (NMDevice *device, gboolean free_tdc)
 
 	if (priv->teamd_pid > 0) {
 		priv->kill_in_progress = TRUE;
-		nm_utils_kill_child_async (priv->teamd_pid, SIGTERM,
-		                           LOGD_TEAM, "teamd",
+		nm_utils_kill_child_async (priv->teamd_pid,
+		                           SIGTERM,
+		                           LOGD_TEAM,
+		                           "teamd",
 		                           2000,
 		                           teamd_kill_cb,
-		                           g_object_ref (device));
+		                           g_object_ref (self));
 		priv->teamd_pid = 0;
 	}
 
-	if (priv->tdc && free_tdc) {
+	if (   priv->tdc
+	    && free_tdc) {
 		teamdctl_disconnect (priv->tdc);
 		teamdctl_free (priv->tdc);
 		priv->tdc = NULL;
@@ -333,7 +323,7 @@ teamd_timeout_cb (gpointer user_data)
 	if (priv->teamd_pid && !priv->tdc) {
 		/* Timed out launching our own teamd process */
 		_LOGW (LOGD_TEAM, "teamd timed out");
-		teamd_cleanup (device, TRUE);
+		teamd_cleanup (self, TRUE);
 
 		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);
@@ -341,7 +331,7 @@ teamd_timeout_cb (gpointer user_data)
 		/* Read again the configuration after the timeout since it might
 		 * have changed.
 		 */
-		if (!teamd_read_config (device)) {
+		if (!teamd_read_config (self)) {
 			_LOGW (LOGD_TEAM, "failed to read teamd configuration");
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 		}
@@ -388,7 +378,7 @@ teamd_dbus_appeared (GDBusConnection *connection,
 		if (ret) {
 			g_variant_get (ret, "(u)", &pid);
 			if (pid != priv->teamd_pid)
-				teamd_cleanup (device, FALSE);
+				teamd_cleanup (self, FALSE);
 		} else {
 			_LOGW (LOGD_TEAM, "failed to determine D-Bus name owner");
 			/* If we can't determine the bus name owner, don't kill our
@@ -403,16 +393,21 @@ teamd_dbus_appeared (GDBusConnection *connection,
 	 * device activation.
 	 */
 	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_sys_iface_state_is_external_or_assume (device)) {
-			teamd_cleanup (device, TRUE);
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
-		}
+
+	if (   nm_device_get_state (device) != NM_DEVICE_STATE_PREPARE
+	    || priv->stage1_state != NM_DEVICE_STAGE_STATE_PENDING)
+		return;
+
+	if (success)
+		success = teamd_read_config (self);
+
+	if (!success) {
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+		return;
 	}
+
+	priv->stage1_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+	nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 static void
@@ -437,15 +432,16 @@ teamd_dbus_vanished (GDBusConnection *dbus_connection,
 	}
 
 	_LOGI (LOGD_TEAM, "teamd vanished from D-Bus");
-	teamd_cleanup (device, TRUE);
+	teamd_cleanup (self, TRUE);
 
 	/* Attempt to respawn teamd */
-	if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_ACTIVATED) {
-		NMConnection *connection = nm_device_get_applied_connection (device);
-
-		g_assert (connection);
-		if (!teamd_start (device, connection))
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	if (   state >= NM_DEVICE_STATE_PREPARE
+	    && state <= NM_DEVICE_STATE_ACTIVATED) {
+		if (!teamd_start (self)) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+		}
 	}
 }
 
@@ -470,7 +466,7 @@ teamd_process_watch_cb (GPid pid, int status, gpointer user_data)
 	    (state >= NM_DEVICE_STATE_PREPARE) &&
 	    (state <= NM_DEVICE_STATE_ACTIVATED)) {
 		_LOGW (LOGD_TEAM, "teamd process %lld quit unexpectedly; failing activation", (long long) pid);
-		teamd_cleanup (device, TRUE);
+		teamd_cleanup (self, TRUE);
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 	}
 }
@@ -525,11 +521,11 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
 }
 
 static gboolean
-teamd_start (NMDevice *device, NMConnection *connection)
+teamd_start (NMDeviceTeam *self)
 {
-	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
-	const char *iface = nm_device_get_ip_iface (device);
+	const char *iface = nm_device_get_ip_iface (NM_DEVICE (self));
+	NMConnection *connection;
 	gs_unref_ptrarray GPtrArray *argv = NULL;
 	gs_free_error GError *error = NULL;
 	gs_free char *tmp_str = NULL;
@@ -540,8 +536,13 @@ teamd_start (NMDevice *device, NMConnection *connection)
 	gs_free char *cloned_mac = NULL;
 	gs_free const char **envp = NULL;
 
+	connection = nm_device_get_applied_connection (NM_DEVICE (self));
+
 	s_team = nm_connection_get_setting_team (connection);
-	g_return_val_if_fail (s_team, FALSE);
+	if (!s_team)
+		g_return_val_if_reached (FALSE);
+
+	nm_assert (iface);
 
 	teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
 	if (!teamd_binary) {
@@ -553,7 +554,7 @@ teamd_start (NMDevice *device, NMConnection *connection)
 		g_warn_if_reached ();
 		if (!priv->teamd_pid)
 			teamd_kill (self, teamd_binary, NULL);
-		teamd_cleanup (device, TRUE);
+		teamd_cleanup (self, TRUE);
 	}
 
 	/* Start teamd now */
@@ -568,7 +569,7 @@ teamd_start (NMDevice *device, NMConnection *connection)
 	g_ptr_array_add (argv, (gpointer) iface);
 
 	config = nm_setting_team_get_config (s_team);
-	if (!nm_device_hw_addr_get_cloned (device, connection, FALSE, &cloned_mac, NULL, &error)) {
+	if (!nm_device_hw_addr_get_cloned (NM_DEVICE (self), connection, FALSE, &cloned_mac, NULL, &error)) {
 		_LOGW (LOGD_DEVICE, "set-hw-addr: %s", error->message);
 		return FALSE;
 	}
@@ -615,18 +616,18 @@ teamd_start (NMDevice *device, NMConnection *connection)
 	if (!g_spawn_async ("/", (char **) argv->pdata, (char **) envp, G_SPAWN_DO_NOT_REAP_CHILD,
 	                    teamd_child_setup, NULL, &priv->teamd_pid, &error)) {
 		_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message);
-		teamd_cleanup (device, TRUE);
+		teamd_cleanup (self, TRUE);
 		return FALSE;
 	}
 
 	/* Start a timeout for teamd to appear at D-Bus */
 	if (!priv->teamd_timeout)
-		priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, device);
+		priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, self);
 
 	/* Monitor the child process so we know when it dies */
 	priv->teamd_process_watch = g_child_watch_add (priv->teamd_pid,
 	                                               teamd_process_watch_cb,
-	                                               device);
+	                                               self);
 
 	_LOGI (LOGD_TEAM, "Activation: (team) started teamd [pid %u]...", (guint) priv->teamd_pid);
 	return TRUE;
@@ -637,21 +638,21 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	gs_free_error GError *error = NULL;
 	NMSettingTeam *s_team;
-	NMConnection *connection;
 	const char *cfg;
 
-	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
+	s_team = nm_device_get_applied_setting (device, NM_TYPE_SETTING_TEAM);
+	if (!s_team)
+		g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE);
 
-	connection = nm_device_get_applied_connection (device);
-	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+	if (priv->stage1_state == NM_DEVICE_STAGE_STATE_PENDING)
+		return NM_ACT_STAGE_RETURN_POSTPONE;
 
-	s_team = nm_connection_get_setting_team (connection);
-	g_return_val_if_fail (s_team, NM_ACT_STAGE_RETURN_FAILURE);
+	if (priv->stage1_state == NM_DEVICE_STAGE_STATE_COMPLETED)
+		return NM_ACT_STAGE_RETURN_SUCCESS;
+
+	priv->stage1_state = NM_DEVICE_STAGE_STATE_PENDING;
 
 	if (priv->tdc) {
 		/* If the existing teamd config is the same as we're about to use,
@@ -660,7 +661,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		 * have a PID, then we must fail.
 		 */
 		cfg = teamdctl_config_get_raw (priv->tdc);
-		if (cfg && nm_streq0 (cfg,  nm_setting_team_get_config (s_team))) {
+		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;
 		}
@@ -675,17 +677,18 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		}
 
 		_LOGD (LOGD_TEAM, "existing teamd config mismatch; respawning...");
-		teamd_cleanup (device, TRUE);
+		teamd_cleanup (self, TRUE);
 	}
 
 	if (priv->kill_in_progress) {
 		_LOGT (LOGD_TEAM, "kill in progress, wait before starting teamd");
-		priv->connection = g_object_ref (connection);
 		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
 
-	return teamd_start (device, connection) ?
-		NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_FAILURE;
+	if (!teamd_start (self))
+		return NM_ACT_STAGE_RETURN_FAILURE;
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 static void
@@ -694,16 +697,19 @@ deactivate (NMDevice *device)
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
+	priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT;
+
 	if (nm_device_sys_iface_state_is_external (device))
 		return;
 
-	if (priv->teamd_pid || priv->tdc)
+	if (   priv->teamd_pid
+	    || priv->tdc)
 		_LOGI (LOGD_TEAM, "deactivation: stopping teamd...");
 
 	if (!priv->teamd_pid)
 		teamd_kill (self, NULL, NULL);
-	teamd_cleanup (device, TRUE);
-	g_clear_object (&priv->connection);
+
+	teamd_cleanup (self, TRUE);
 }
 
 static gboolean
@@ -773,27 +779,27 @@ release_slave (NMDevice *device,
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
-	gboolean success;
+	gboolean do_release, success;
+	NMSettingTeamPort *s_port;
 	int ifindex_slave;
 	int ifindex;
 
-	ifindex = nm_device_get_ifindex (device);
-	if (   ifindex <= 0
-	    || !nm_platform_link_get (nm_device_get_platform (device), ifindex))
-		configure = FALSE;
+	do_release = configure;
+	if (do_release) {
+		ifindex = nm_device_get_ifindex (device);
+		if (   ifindex <= 0
+		    || !nm_platform_link_get (nm_device_get_platform (device), ifindex))
+			do_release = FALSE;
+	}
 
 	ifindex_slave = nm_device_get_ip_ifindex (slave);
 
 	if (ifindex_slave <= 0) {
 		_LOGD (LOGD_TEAM, "team port %s is already released", nm_device_get_ip_iface (slave));
-		return;
-	}
-
-	if (configure) {
+	} else if (do_release) {
 		success = nm_platform_link_release (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    ifindex_slave);
-
 		if (success)
 			_LOGI (LOGD_TEAM, "released team port %s", nm_device_get_ip_iface (slave));
 		else
@@ -814,6 +820,13 @@ release_slave (NMDevice *device,
 		                                                  self);
 	} else
 		_LOGI (LOGD_TEAM, "team port %s was released", nm_device_get_ip_iface (slave));
+
+	/* Delete any port configuration we previously set */
+	if (   configure
+	    && priv->tdc
+	    && (s_port = nm_device_get_applied_setting (slave, NM_TYPE_SETTING_TEAM_PORT))
+	    && (nm_setting_team_port_get_config (s_port)))
+		teamdctl_port_config_update_raw (priv->tdc, nm_device_get_ip_iface (slave), "{}");
 }
 
 static gboolean
@@ -869,7 +882,7 @@ static void
 constructed (GObject *object)
 {
 	NMDevice *device = NM_DEVICE (object);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE ((NMDeviceTeam *) device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 	char *tmp_str = NULL;
 
 	G_OBJECT_CLASS (nm_device_team_parent_class)->constructed (object);
@@ -901,15 +914,15 @@ nm_device_team_new (const char *iface)
 static void
 dispose (GObject *object)
 {
-	NMDevice *device = NM_DEVICE (object);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE ((NMDeviceTeam *) device);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (object);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
 	if (priv->teamd_dbus_watch) {
 		g_bus_unwatch_name (priv->teamd_dbus_watch);
 		priv->teamd_dbus_watch = 0;
 	}
 
-	teamd_cleanup (device, TRUE);
+	teamd_cleanup (self, TRUE);
 	g_clear_pointer (&priv->config, g_free);
 
 	G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object);
diff --git a/src/devices/team/nm-device-team.h b/src/devices/team/nm-device-team.h
index 0c0b5101..71312c07 100644
--- a/src/devices/team/nm-device-team.h
+++ b/src/devices/team/nm-device-team.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Jiri Pirko <jiri@resnulli.us>
- *
- * 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.
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_TEAM_H__
diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c
index 21a85701..3768217b 100644
--- a/src/devices/team/nm-team-factory.c
+++ b/src/devices/team/nm-team-factory.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -58,7 +44,7 @@ NM_DEVICE_FACTORY_DECLARE_TYPES (
 G_MODULE_EXPORT NMDeviceFactory *
 nm_device_factory_create (GError **error)
 {
-	nm_manager_set_capability (nm_manager_get (), NM_CAPABILITY_TEAM);
+	nm_manager_set_capability (NM_MANAGER_GET, NM_CAPABILITY_TEAM);
 	return (NMDeviceFactory *) g_object_new (NM_TYPE_TEAM_FACTORY, NULL);
 }
 
diff --git a/src/devices/tests/meson.build b/src/devices/tests/meson.build
index 4b4c5c7e..bb53d1ee 100644
--- a/src/devices/tests/meson.build
+++ b/src/devices/tests/meson.build
@@ -7,8 +7,10 @@ foreach test_unit: test_units
   exe = executable(
     test_unit,
     test_unit + '.c',
-    dependencies: test_nm_dep,
+    dependencies: libnetwork_manager_test_dep,
+    c_args: test_c_flags,
   )
+
   test(
     'devices/' + test_unit,
     test_script,
diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-acd.c
index 42accfaf..da5e4f2d 100644
--- a/src/devices/tests/test-acd.c
+++ b/src/devices/tests/test-acd.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index ad157b7e..bd71c9e3 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build
index 4dfbe4c8..6566f201 100644
--- a/src/devices/wifi/meson.build
+++ b/src/devices/wifi/meson.build
@@ -19,14 +19,11 @@ if enable_iwd
   )
 endif
 
-deps = [
-  nm_dep,
-]
-
 libnm_device_plugin_wifi = shared_module(
   'nm-device-plugin-wifi',
   sources: sources,
-  dependencies: deps,
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
@@ -41,13 +38,20 @@ test(
   args: [libnm_device_plugin_wifi.full_path(), linker_script_devices],
 )
 
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-wifi: src/devices/wifi/libnm-device-plugin-wifi.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wifi/.libs/libnm-device-plugin-wifi.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wifi/.libs/libnm-device-plugin-wifi.so)
-'''
-
 if enable_tests
-  subdir('tests')
+  test_unit = 'test-devices-wifi'
+
+  exe = executable(
+    test_unit,
+    ['tests/' + test_unit + '.c'] + common_sources,
+    dependencies: libnetwork_manager_test_dep,
+    c_args: test_c_flags,
+  )
+
+  test(
+    test_unit,
+    test_script,
+    args: test_args + [exe.full_path()],
+    timeout: default_test_timeout,
+  )
 endif
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index 0e6759e1..6b587e3f 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Intel Corporation
  */
 
@@ -1714,18 +1700,14 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceIwd *self = NM_DEVICE_IWD (device);
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMActStageReturn ret;
 	NMWifiAP *ap = NULL;
+	gs_unref_object NMWifiAP *ap_fake = NULL;
 	NMActRequest *req;
 	NMConnection *connection;
 	NMSettingWireless *s_wireless;
 	const char *mode;
 	const char *ap_path;
 
-	ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	req = nm_device_get_act_request (device);
 	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
@@ -1741,7 +1723,9 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		goto add_new;
 
 	ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
-	ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL;
+	ap =   ap_path
+	     ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path)
+	     : NULL;
 	if (ap) {
 		set_current_ap (self, ap, TRUE);
 		return NM_ACT_STAGE_RETURN_SUCCESS;
@@ -1767,19 +1751,19 @@ add_new:
 	 * until the real one is found in the scan list (Ad-Hoc or Hidden), or until
 	 * the device is deactivated (Ad-Hoc or Hotspot).
 	 */
-	ap = nm_wifi_ap_new_fake_from_connection (connection);
-	g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	ap_fake = nm_wifi_ap_new_fake_from_connection (connection);
+	if (!ap_fake)
+		g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE);
 
-	if (nm_wifi_ap_is_hotspot (ap))
-		nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device));
+	if (nm_wifi_ap_is_hotspot (ap_fake))
+		nm_wifi_ap_set_address (ap_fake, nm_device_get_hw_address (device));
 
 	g_object_freeze_notify (G_OBJECT (self));
-	ap_add_remove (self, TRUE, ap, FALSE);
+	ap_add_remove (self, TRUE, ap_fake, FALSE);
 	g_object_thaw_notify (G_OBJECT (self));
-	set_current_ap (self, ap, FALSE);
+	set_current_ap (self, ap_fake, FALSE);
 	nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-	                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
-	g_object_unref (ap);
+	                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (ap_fake)));
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h
index aab45b7b..586e02f4 100644
--- a/src/devices/wifi/nm-device-iwd.h
+++ b/src/devices/wifi/nm-device-iwd.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Intel Corporation
  */
 
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index aa50c420..c19ec766 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -1,26 +1,11 @@
-/* NetworkManager -- Network link manager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Dan Williams <dcbw@redhat.com>
  * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
  * Daniel Drake <dsd@laptop.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2005 - 2014 Red Hat, Inc.
- * (C) Copyright 2008 Collabora Ltd.
- * (C) Copyright 2009 One Laptop per Child
+ * Copyright (C) 2005 - 2014 Red Hat, Inc.
+ * Copyright (C) 2008 Collabora Ltd.
+ * Copyright (C) 2009 One Laptop per Child
  */
 
 #include "nm-default.h"
@@ -58,7 +43,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceOlpcMesh,
 typedef struct {
 	NMDevice *companion;
 	NMManager *manager;
-	gboolean  stage1_waiting;
+	bool stage1_waiting:1;
 } NMDeviceOlpcMeshPrivate;
 
 struct _NMDeviceOlpcMesh {
@@ -145,13 +130,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
-	NMActStageReturn ret;
 	gboolean scanning;
 
-	ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	/* disconnect companion device, if it is connected */
 	if (nm_device_get_act_request (NM_DEVICE (priv->companion))) {
 		_LOGI (LOGD_OLPC, "disconnecting companion device %s",
@@ -171,6 +151,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
 
+	priv->stage1_waiting = FALSE;
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
@@ -273,10 +254,9 @@ companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_d
 		return;
 
 	g_object_get (companion, NM_DEVICE_WIFI_SCANNING, &scanning, NULL);
-
 	if (!scanning) {
 		priv->stage1_waiting = FALSE;
-		nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self));
+		nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
 	}
 }
 
@@ -469,7 +449,7 @@ constructed (GObject *object)
 
 	G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->constructed (object);
 
-	priv->manager = g_object_ref (nm_manager_get ());
+	priv->manager = g_object_ref (NM_MANAGER_GET);
 
 	g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added_cb), self);
 	g_signal_connect (priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK (device_removed_cb), self);
diff --git a/src/devices/wifi/nm-device-olpc-mesh.h b/src/devices/wifi/nm-device-olpc-mesh.h
index 619fc46a..e6e76d6f 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.h
+++ b/src/devices/wifi/nm-device-olpc-mesh.h
@@ -1,26 +1,11 @@
-/* NetworkManager -- Network link manager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Dan Williams <dcbw@redhat.com>
  * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
  * Daniel Drake <dsd@laptop.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2005 Red Hat, Inc.
- * (C) Copyright 2008 Collabora Ltd.
- * (C) Copyright 2009 One Laptop per Child
+ * Copyright (C) 2005 Red Hat, Inc.
+ * Copyright (C) 2008 Collabora Ltd.
+ * Copyright (C) 2009 One Laptop per Child
  */
 
 #ifndef __NETWORKMANAGER_DEVICE_OLPC_MESH_H__
diff --git a/src/devices/wifi/nm-device-wifi-p2p.c b/src/devices/wifi/nm-device-wifi-p2p.c
index 649f36d0..34ff70fa 100644
--- a/src/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/devices/wifi/nm-device-wifi-p2p.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Wi-Fi P2P Device
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -63,6 +48,7 @@ typedef struct {
 
 	CList peers_lst_head;
 
+	guint find_peer_timeout_id;
 	guint sup_timeout_id;
 	guint peer_dump_id;
 	guint peer_missing_id;
@@ -349,7 +335,7 @@ supplicant_find_timeout_cb (gpointer user_data)
 	NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data);
 	NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
 
-	priv->sup_timeout_id = 0;
+	priv->find_peer_timeout_id = 0;
 
 	nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface);
 
@@ -368,27 +354,16 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device);
 	NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
-	NMActStageReturn ret;
-	NMActRequest *req;
 	NMConnection *connection;
 	NMSettingWifiP2P *s_wifi_p2p;
 	NMWifiP2PPeer *peer;
 
-	nm_clear_g_source (&priv->sup_timeout_id);
-
-	ret = NM_DEVICE_CLASS (nm_device_wifi_p2p_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	if (!priv->mgmt_iface) {
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	req = nm_device_get_act_request (NM_DEVICE (self));
-	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
-
-	connection = nm_act_request_get_applied_connection (req);
+	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P));
@@ -397,33 +372,19 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection);
 	if (!peer) {
 		/* Set up a timeout on the find attempt and run a find for the same period of time */
-		priv->sup_timeout_id = g_timeout_add_seconds (10,
-		                                              supplicant_find_timeout_cb,
-		                                              self);
-
-		nm_supplicant_interface_p2p_start_find (priv->mgmt_iface, 10);
+		if (priv->find_peer_timeout_id == 0) {
+			priv->find_peer_timeout_id = g_timeout_add_seconds (10,
+			                                                    supplicant_find_timeout_cb,
+			                                                    self);
 
+			nm_supplicant_interface_p2p_start_find (priv->mgmt_iface, 10);
+		}
 		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
 
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
-static void
-cleanup_p2p_connect_attempt (NMDeviceWifiP2P *self, gboolean disconnect)
-{
-	NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
-
-	nm_clear_g_source (&priv->sup_timeout_id);
-	nm_clear_g_source (&priv->peer_missing_id);
-
-	if (priv->mgmt_iface)
-		nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface);
-
-	if (disconnect && priv->group_iface)
-		nm_supplicant_interface_p2p_disconnect (priv->group_iface);
-}
-
 /*
  * supplicant_connection_timeout_cb
  *
@@ -461,7 +422,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	NMWifiP2PPeer *peer;
 	GBytes *wfd_ies;
 
-	nm_clear_g_source (&priv->sup_timeout_id);
+	if (nm_clear_g_source (&priv->find_peer_timeout_id))
+		nm_assert_not_reached ();
 
 	if (!priv->mgmt_iface) {
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
@@ -493,9 +455,11 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	                                     "pbc", NULL);
 
 	/* Set up a timeout on the connect attempt */
-	priv->sup_timeout_id = g_timeout_add_seconds (45,
-	                                              supplicant_connection_timeout_cb,
-	                                              self);
+	if (priv->sup_timeout_id == 0) {
+		priv->sup_timeout_id = g_timeout_add_seconds (45,
+		                                              supplicant_connection_timeout_cb,
+		                                              self);
+	}
 
 	/* We'll get stage3 started when the P2P group has been started */
 	return NM_ACT_STAGE_RETURN_POSTPONE;
@@ -550,17 +514,19 @@ peer_add_remove (NMDeviceWifiP2P *self,
 	if (is_adding) {
 		/* If we are in prepare state, then we are currently runnign a find
 		 * to search for the requested peer. */
-		if (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE) {
+		if (priv->find_peer_timeout_id != 0) {
 			NMConnection *connection;
 
+			nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE);
+
 			connection = nm_device_get_applied_connection (device);
-			g_assert (connection);
+			nm_assert (NM_IS_CONNECTION (connection));
 
 			peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection);
 			if (peer) {
 				/* A peer for the connection was found, cancel the timeout and go to configure state. */
-				nm_clear_g_source (&priv->sup_timeout_id);
-				nm_device_activate_schedule_stage2_device_config (device);
+				nm_clear_g_source (&priv->find_peer_timeout_id);
+				nm_device_activate_schedule_stage1_device_prepare (device);
 			}
 		}
 
@@ -620,8 +586,17 @@ deactivate (NMDevice *device)
 {
 	NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device);
 	int ifindex = nm_device_get_ip_ifindex (device);
+	NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
+
+	nm_clear_g_source (&priv->find_peer_timeout_id);
+	nm_clear_g_source (&priv->sup_timeout_id);
+	nm_clear_g_source (&priv->peer_missing_id);
 
-	cleanup_p2p_connect_attempt (self, TRUE);
+	if (priv->mgmt_iface)
+		nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface);
+
+	if (priv->group_iface)
+		nm_supplicant_interface_p2p_disconnect (priv->group_iface);
 
 	/* Clear any critical protocol notification in the Wi-Fi stack */
 	if (ifindex > 0)
@@ -922,6 +897,7 @@ supplicant_interfaces_release (NMDeviceWifiP2P *self, gboolean set_is_waiting)
 		nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, NULL);
 		g_signal_handlers_disconnect_by_data (priv->mgmt_iface, self);
 		g_clear_object (&priv->mgmt_iface);
+		nm_clear_g_source (&priv->find_peer_timeout_id);
 		nm_clear_g_source (&priv->sup_timeout_id);
 	}
 
diff --git a/src/devices/wifi/nm-device-wifi-p2p.h b/src/devices/wifi/nm-device-wifi-p2p.h
index a13eef15..df25ec65 100644
--- a/src/devices/wifi/nm-device-wifi-p2p.h
+++ b/src/devices/wifi/nm-device-wifi-p2p.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Wi-Fi P2P Device
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_DEVICE_WIFI_P2P_H__
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index f690100b..65ba2bcc 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -411,7 +397,9 @@ set_current_ap (NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available
 		NM80211Mode mode = nm_wifi_ap_get_mode (old_ap);
 
 		/* Remove any AP from the internal list if it was created by NM or isn't known to the supplicant */
-		if (mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_wifi_ap_get_fake (old_ap))
+		if (   NM_IN_SET (mode, NM_802_11_MODE_ADHOC,
+		                        NM_802_11_MODE_AP)
+		    || nm_wifi_ap_get_fake (old_ap))
 			ap_add_remove (self, FALSE, old_ap, recheck_available_connections);
 		g_object_unref (old_ap);
 	}
@@ -645,36 +633,6 @@ deactivate_reset_hw_addr (NMDevice *device)
 }
 
 static gboolean
-is_adhoc_wpa (NMConnection *connection)
-{
-	NMSettingWireless *s_wifi;
-	NMSettingWirelessSecurity *s_wsec;
-	const char *mode, *key_mgmt;
-
-	/* 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
-	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
-	 */
-
-	s_wifi = nm_connection_get_setting_wireless (connection);
-	g_return_val_if_fail (s_wifi != NULL, FALSE);
-
-	mode = nm_setting_wireless_get_mode (s_wifi);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) != 0)
-		return FALSE;
-
-	s_wsec = nm_connection_get_setting_wireless_security (connection);
-	if (!s_wsec)
-		return FALSE;
-
-	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
-	if (g_strcmp0 (key_mgmt, "wpa-none") != 0)
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
@@ -720,12 +678,6 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError
 		return FALSE;
 	}
 
-	if (is_adhoc_wpa (connection)) {
-		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
-		                            "Ad-Hoc WPA networks are not supported");
-		return FALSE;
-	}
-
 	/* Early exit if supplicant or device doesn't support requested mode */
 	mode = nm_setting_wireless_get_mode (s_wireless);
 	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) {
@@ -953,19 +905,6 @@ complete_connection (NMDevice *device,
 			return FALSE;
 	}
 
-	/* 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
-	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
-	 */
-	if (is_adhoc_wpa (connection)) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_SETTING,
-		                     _("WPA Ad-Hoc disabled due to kernel bugs"));
-		g_prefix_error (error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
-		return FALSE;
-	}
-
 	ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
 	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
@@ -1278,7 +1217,8 @@ scanning_prohibited (NMDeviceWifi *self, gboolean periodic)
 	/* Don't scan when a an AP or Ad-Hoc connection is active as it will
 	 * disrupt connected clients or peers.
 	 */
-	if (priv->mode == NM_802_11_MODE_ADHOC || priv->mode == NM_802_11_MODE_AP)
+	if (NM_IN_SET (priv->mode, NM_802_11_MODE_ADHOC,
+	                           NM_802_11_MODE_AP))
 		return TRUE;
 
 	switch (nm_device_get_state (NM_DEVICE (self))) {
@@ -1781,8 +1721,10 @@ wifi_secrets_cb (NMActRequest *req,
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-	} else
-		nm_device_activate_schedule_stage1_device_prepare (device);
+		return;
+	}
+
+	nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 static void
@@ -1801,10 +1743,11 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
                                      NMDeviceWifi *self)
 {
 	NMActRequest *req;
-	GVariant *val, *secrets = NULL;
+	gs_unref_variant GVariant *val_key = NULL;
+	gs_unref_variant GVariant *secrets = NULL;
+	gs_free_error GError *error = NULL;
 	const char *array;
 	gsize psk_len = 0;
-	GError *error = NULL;
 
 	if (nm_device_get_state (NM_DEVICE (self)) != NM_DEVICE_STATE_NEED_AUTH) {
 		_LOGI (LOGD_DEVICE | LOGD_WIFI, "WPS: The connection can't be updated with credentials");
@@ -1816,11 +1759,11 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
 	req = nm_device_get_act_request (NM_DEVICE (self));
 	g_return_if_fail (NM_IS_ACT_REQUEST (req));
 
-	val = g_variant_lookup_value (credentials, "Key", G_VARIANT_TYPE_BYTESTRING);
-	if (val) {
+	val_key = g_variant_lookup_value (credentials, "Key", G_VARIANT_TYPE_BYTESTRING);
+	if (val_key) {
 		char psk[64];
 
-		array = g_variant_get_fixed_array (val, &psk_len, 1);
+		array = g_variant_get_fixed_array (val_key, &psk_len, 1);
 		if (psk_len >= 8 && psk_len <= 63) {
 			memcpy (psk, array, psk_len);
 			psk[psk_len] = '\0';
@@ -1833,22 +1776,22 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
 		}
 		if (!secrets)
 			_LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: ignore invalid PSK");
-		g_variant_unref (val);
-	}
-	if (secrets) {
-		if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req),
-		                                        nm_act_request_get_applied_connection (req),
-		                                        NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-		                                        secrets,
-		                                        &error)) {
-			wifi_secrets_cancel (self);
-			nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
-		} else {
-			_LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: Could not update the connection with credentials: %s", error->message);
-			g_error_free (error);
-		}
-		g_variant_unref (secrets);
 	}
+
+	if (!secrets)
+		return;
+
+	if (!nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req),
+	                                         nm_act_request_get_applied_connection (req),
+	                                         NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+	                                         secrets,
+	                                         &error)) {
+		_LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: Could not update the connection with credentials: %s", error->message);
+		return;
+	}
+
+	wifi_secrets_cancel (self);
+	nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
 }
 
 static gboolean
@@ -2458,9 +2401,9 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
-	if (   priv->mode == NM_802_11_MODE_ADHOC
-	    || priv->mode == NM_802_11_MODE_MESH
-	    || priv->mode == NM_802_11_MODE_AP) {
+	if (NM_IN_SET (priv->mode, NM_802_11_MODE_ADHOC,
+	                           NM_802_11_MODE_MESH,
+	                           NM_802_11_MODE_AP)) {
 		/* In Ad-Hoc and AP modes there's nothing to check the encryption key
 		 * (if any), so supplicant timeouts here are almost certainly the wifi
 		 * driver being really stupid.
@@ -2666,18 +2609,14 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMActStageReturn ret;
 	NMWifiAP *ap = NULL;
+	gs_unref_object NMWifiAP *ap_fake = NULL;
 	NMActRequest *req;
 	NMConnection *connection;
 	NMSettingWireless *s_wireless;
 	const char *mode;
 	const char *ap_path;
 
-	ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	req = nm_device_get_act_request (NM_DEVICE (self));
 	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
@@ -2703,62 +2642,49 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		priv->mode = NM_802_11_MODE_MESH;
 	_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
-	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
-	 */
-	if (is_adhoc_wpa (connection)) {
-		_LOGW (LOGD_WIFI, "Ad-Hoc WPA disabled due to kernel bugs");
-		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
-
 	/* expire the temporary MAC address used during scanning */
 	priv->hw_addr_scan_expire = 0;
 
 	/* Set spoof MAC to the interface */
-	if (!nm_device_hw_addr_set_cloned (device, connection, TRUE))
+	if (!nm_device_hw_addr_set_cloned (device, connection, TRUE)) {
+		*out_failure_reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
 		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
 	/* AP and Mesh modes never use a specific object or existing scanned AP */
-	if (priv->mode != NM_802_11_MODE_AP && priv->mode != NM_802_11_MODE_MESH) {
+	if (!NM_IN_SET (priv->mode, NM_802_11_MODE_AP,
+	                            NM_802_11_MODE_MESH)) {
 		ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
-		ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL;
-		if (ap)
-			goto done;
-
-		ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+		ap =   ap_path
+		     ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path)
+		     : NULL;
 	}
+	if (!ap)
+		ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
 
-	if (ap) {
-		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-		                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
-		goto done;
-	}
+	if (!ap) {
+		/* If the user is trying to connect to an AP that NM doesn't yet know about
+		 * (hidden network or something), starting a Hotspot or joining a Mesh,
+		 * create a fake APfrom the security settings in the connection.  This "fake"
+		 * AP gets used until the real one is found in the scan list (Ad-Hoc or Hidden),
+		 * or until the device is deactivated (Hotspot).
+		 */
+		ap_fake = nm_wifi_ap_new_fake_from_connection (connection);
+		if (!ap_fake)
+			g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE);
 
-	/* If the user is trying to connect to an AP that NM doesn't yet know about
-	 * (hidden network or something), starting a Hotspot or joining a Mesh,
-	 * create a fake APfrom the security settings in the connection.  This "fake"
-	 * AP gets used until the real one is found in the scan list (Ad-Hoc or Hidden),
-	 * or until the device is deactivated (Hotspot).
-	 */
-	ap = nm_wifi_ap_new_fake_from_connection (connection);
-	g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+		if (nm_wifi_ap_is_hotspot (ap_fake))
+			nm_wifi_ap_set_address (ap_fake, nm_device_get_hw_address (device));
 
-	if (nm_wifi_ap_is_hotspot (ap))
-		nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device));
+		g_object_freeze_notify (G_OBJECT (self));
+		ap_add_remove (self, TRUE, ap_fake, TRUE);
+		g_object_thaw_notify (G_OBJECT (self));
+		ap = ap_fake;
+	}
 
-	g_object_freeze_notify (G_OBJECT (self));
-	ap_add_remove (self, TRUE, ap, TRUE);
-	g_object_thaw_notify (G_OBJECT (self));
 	set_current_ap (self, ap, FALSE);
 	nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
 	                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
-	g_object_unref (ap);
-	return NM_ACT_STAGE_RETURN_SUCCESS;
-
-done:
-	set_current_ap (self, ap, TRUE);
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
@@ -2893,8 +2819,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	 * if the user didn't specify one and we didn't find an AP that matched
 	 * the connection, just pick a frequency the device supports.
 	 */
-	if (   ap_mode == NM_802_11_MODE_ADHOC
-	    || ap_mode == NM_802_11_MODE_MESH
+	if (   NM_IN_SET (ap_mode, NM_802_11_MODE_ADHOC,
+	                           NM_802_11_MODE_MESH)
 	    || nm_wifi_ap_is_hotspot (ap))
 		ensure_hotspot_frequency (self, s_wireless, ap);
 
@@ -3242,6 +3168,15 @@ set_enabled (NMDevice *device, gboolean enabled)
 }
 
 static gboolean
+get_guessed_metered (NMDevice *device)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	return priv->current_ap && nm_wifi_ap_get_metered (priv->current_ap);
+}
+
+static gboolean
 can_reapply_change (NMDevice *device,
                     const char *setting_name,
                     NMSetting *s_old,
@@ -3274,6 +3209,7 @@ static void
 reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_new)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceState state = nm_device_get_state (device);
 
 	NM_DEVICE_CLASS (nm_device_wifi_parent_class)->reapply_connection (device,
 	                                                                   con_old,
@@ -3281,7 +3217,8 @@ reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_n
 
 	_LOGD (LOGD_DEVICE, "reapplying wireless settings");
 
-	if (!wake_on_wlan_enable (self))
+	if (   state >= NM_DEVICE_STATE_CONFIG
+	    && !wake_on_wlan_enable (self))
 		_LOGW (LOGD_DEVICE | LOGD_WIFI, "Cannot configure WoWLAN.");
 }
 
@@ -3451,6 +3388,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	device_class->check_connection_available = check_connection_available;
 	device_class->complete_connection = complete_connection;
 	device_class->get_enabled = get_enabled;
+	device_class->get_guessed_metered = get_guessed_metered;
 	device_class->set_enabled = set_enabled;
 
 	device_class->act_stage1_prepare = act_stage1_prepare;
diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h
index 82f62be6..aaf47143 100644
--- a/src/devices/wifi/nm-device-wifi.h
+++ b/src/devices/wifi/nm-device-wifi.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2016 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index c79f6cc6..470cb1c9 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Intel Corporation
  */
 
@@ -912,7 +898,7 @@ nm_iwd_manager_init (NMIwdManager *self)
 {
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
 
-	priv->manager = g_object_ref (nm_manager_get ());
+	priv->manager = g_object_ref (NM_MANAGER_GET);
 	g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED,
 	                  G_CALLBACK (device_added), self);
 
diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h
index b410e4ce..c50963fe 100644
--- a/src/devices/wifi/nm-iwd-manager.h
+++ b/src/devices/wifi/nm-iwd-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Intel Corporation
  */
 
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index c7ab7f04..ee7dc236 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -67,10 +53,12 @@ struct _NMWifiAPPrivate {
 	NM80211ApSecurityFlags wpa_flags;  /* WPA-related flags */
 	NM80211ApSecurityFlags rsn_flags;  /* RSN (WPA2) -related flags */
 
+	bool               metered:1;
+
 	/* Non-scanned attributes */
-	bool                fake:1;       /* Whether or not the AP is from a scan */
-	bool                hotspot:1;    /* Whether the AP is a local device's hotspot network */
-	gint32              last_seen;    /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
+	bool               fake:1;       /* Whether or not the AP is from a scan */
+	bool               hotspot:1;    /* Whether the AP is a local device's hotspot network */
+	gint32             last_seen;    /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
 };
 
 typedef struct _NMWifiAPPrivate NMWifiAPPrivate;
@@ -406,6 +394,12 @@ nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen)
 	return FALSE;
 }
 
+gboolean
+nm_wifi_ap_get_metered (const NMWifiAP *self)
+{
+	return NM_WIFI_AP_GET_PRIVATE (self)->metered;
+}
+
 /*****************************************************************************/
 
 static NM80211ApSecurityFlags
@@ -731,44 +725,50 @@ get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate)
 /* Management Frame Information Element IDs, ieee80211_eid */
 #define WLAN_EID_HT_CAPABILITY       45
 #define WLAN_EID_VHT_CAPABILITY     191
+#define WLAN_EID_VENDOR_SPECIFIC    221
 
-static guint32
-get_max_rate (const guint8 *bytes, gsize len)
+static void
+parse_ies (const guint8 *bytes, gsize len, guint32 *out_max_rate, gboolean *out_metered)
 {
 	guint8 id, elem_len;
-	guint32 max_rate = 0;
+	guint32 m;
 
-	while (len) {
-		guint32 m;
+	*out_max_rate = 0;
+	*out_metered = FALSE;
 
+	while (len) {
 		if (len < 2)
-			return 0;
+			break;
 
 		id = *bytes++;
 		elem_len = *bytes++;
 		len -= 2;
 
 		if (elem_len > len)
-			return 0;
+			break;
 
 		switch (id) {
 		case WLAN_EID_HT_CAPABILITY:
-			if (!get_max_rate_ht (bytes, elem_len, &m))
-				return 0;
-			max_rate = NM_MAX (max_rate, m);
+			if (get_max_rate_ht (bytes, elem_len, &m))
+				*out_max_rate = NM_MAX (*out_max_rate, m);
 			break;
 		case WLAN_EID_VHT_CAPABILITY:
-			if (!get_max_rate_vht (bytes, elem_len, &m))
-				return 0;
-			max_rate = NM_MAX (max_rate, m);
+			if (get_max_rate_vht (bytes, elem_len, &m))
+				*out_max_rate = NM_MAX (*out_max_rate, m);
+			break;
+		case WLAN_EID_VENDOR_SPECIFIC:
+			if (   len == 8
+			    && bytes[0] == 0x00            /* OUI: Microsoft */
+			    && bytes[1] == 0x50
+			    && bytes[2] == 0xf2
+			    && bytes[3] == 0x11)           /* OUI type: Network cost */
+				*out_metered = (bytes[7] > 1); /* Cost level > 1 */
 			break;
 		}
 
 		len -= elem_len;
 		bytes += elem_len;
 	}
-
-	return max_rate;
 }
 
 /*****************************************************************************/
@@ -788,7 +788,8 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
 	gint16 i16;
 	guint16 u16;
 	gboolean changed = FALSE;
-	guint32 max_rate;
+	gboolean metered;
+	guint32 max_rate, rate;
 
 	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 	g_return_val_if_fail (properties, FALSE);
@@ -869,9 +870,12 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
 	v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING);
 	if (v) {
 		bytes = g_variant_get_fixed_array (v, &len, 1);
-		max_rate = NM_MAX (max_rate, get_max_rate (bytes, len));
+		parse_ies (bytes, len, &rate, &metered);
+		max_rate = NM_MAX (max_rate, rate);
 		g_variant_unref (v);
+		priv->metered = metered;
 	}
+
 	if (max_rate)
 		changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000);
 
@@ -1002,7 +1006,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 		export_path = "/";
 
 	g_snprintf (str_buf, buf_len,
-	            "%17s %-35s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
+	            "%17s %-35s [ %c %3u %3u%% %c%c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
 	            priv->address ?: "(none)",
 	            (ssid_to_free = _nm_utils_ssid_to_string (priv->ssid)),
 	            (priv->mode == NM_802_11_MODE_ADHOC
@@ -1017,6 +1021,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 	            chan,
 	            priv->strength,
 	            priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_',
+	            priv->metered ? 'M' : '_',
 	            priv->wpa_flags & 0xFFFF,
 	            priv->rsn_flags & 0xFFFF,
 	            priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1,
@@ -1232,7 +1237,7 @@ nm_wifi_ap_new_fake_from_connection (NMConnection *connection)
 	const char *mode, *band, *key_mgmt;
 	guint32 channel;
 	NM80211ApSecurityFlags flags;
-	gboolean psk = FALSE, eap = FALSE;
+	gboolean psk = FALSE, eap = FALSE, adhoc = FALSE;
 
 	g_return_val_if_fail (connection != NULL, NULL);
 
@@ -1252,9 +1257,10 @@ nm_wifi_ap_new_fake_from_connection (NMConnection *connection)
 	if (mode) {
 		if (!strcmp (mode, "infrastructure"))
 			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA);
-		else if (!strcmp (mode, "adhoc"))
+		else if (!strcmp (mode, "adhoc")) {
 			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
-		else if (!strcmp (mode, "mesh"))
+			adhoc = TRUE;
+		} else if (!strcmp (mode, "mesh"))
 			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_MESH);
 		else if (!strcmp (mode, "ap")) {
 			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA);
@@ -1293,7 +1299,7 @@ nm_wifi_ap_new_fake_from_connection (NMConnection *connection)
 
 	psk = !strcmp (key_mgmt, "wpa-psk");
 	eap = !strcmp (key_mgmt, "wpa-eap");
-	if (psk || eap) {
+	if (!adhoc && (psk || eap)) {
 		if (has_proto (s_wireless_sec, PROTO_WPA)) {
 			flags = priv->wpa_flags | (eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK);
 			nm_wifi_ap_set_wpa_flags (ap, flags);
@@ -1305,42 +1311,27 @@ nm_wifi_ap_new_fake_from_connection (NMConnection *connection)
 
 		add_pair_ciphers (ap, s_wireless_sec);
 		add_group_ciphers (ap, s_wireless_sec);
-	} else if (!strcmp (key_mgmt, "wpa-none")) {
-		guint32 i;
-
-		/* Ad-Hoc has special requirements: proto=WPA, pairwise=(none), and
-		 * group=TKIP/CCMP (but not both).
+	} else if (adhoc && psk) {
+		/* Ad-Hoc has special requirements: proto=RSN, pairwise=CCMP and
+		 * group=CCMP.
 		 */
-
 		flags = priv->wpa_flags | NM_802_11_AP_SEC_KEY_MGMT_PSK;
 
-		/* Clear ciphers; pairwise must be unset anyway, and group gets set below */
+		/* Clear ciphers; only CCMP is supported */
 		flags &= ~(  NM_802_11_AP_SEC_PAIR_WEP40
 		           | NM_802_11_AP_SEC_PAIR_WEP104
 		           | NM_802_11_AP_SEC_PAIR_TKIP
-		           | NM_802_11_AP_SEC_PAIR_CCMP
 		           | NM_802_11_AP_SEC_GROUP_WEP40
 		           | NM_802_11_AP_SEC_GROUP_WEP104
-		           | NM_802_11_AP_SEC_GROUP_TKIP
-		           | NM_802_11_AP_SEC_GROUP_CCMP);
+		           | NM_802_11_AP_SEC_GROUP_TKIP);
 
-		for (i = 0; i < nm_setting_wireless_security_get_num_groups (s_wireless_sec); i++) {
-			if (!strcmp (nm_setting_wireless_security_get_group (s_wireless_sec, i), "ccmp")) {
-				flags |= NM_802_11_AP_SEC_GROUP_CCMP;
-				break;
-			}
-		}
-
-		/* Default to TKIP since not all WPA-capable cards can do CCMP */
-		if (!(flags & NM_802_11_AP_SEC_GROUP_CCMP))
-			flags |= NM_802_11_AP_SEC_GROUP_TKIP;
+		flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+		flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+		nm_wifi_ap_set_rsn_flags (ap, flags);
 
-		nm_wifi_ap_set_wpa_flags (ap, flags);
-
-		/* Don't use Ad-Hoc RSN yet */
-		nm_wifi_ap_set_rsn_flags (ap, NM_802_11_AP_SEC_NONE);
+		/* Don't use Ad-Hoc WPA (WPA-none) anymore */
+		nm_wifi_ap_set_wpa_flags (ap, NM_802_11_AP_SEC_NONE);
 	}
-
 done:
 	return ap;
 
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 755e722c..472dfdf9 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -95,6 +81,7 @@ gboolean          nm_wifi_ap_get_fake                 (const NMWifiAP *ap);
 gboolean          nm_wifi_ap_set_fake                 (NMWifiAP *ap,
                                                        gboolean fake);
 NM80211ApFlags    nm_wifi_ap_get_flags                (const NMWifiAP *self);
+gboolean          nm_wifi_ap_get_metered              (const NMWifiAP *self);
 
 const char       *nm_wifi_ap_to_string                (const NMWifiAP *self,
                                                        char *str_buf,
diff --git a/src/devices/wifi/nm-wifi-common.c b/src/devices/wifi/nm-wifi-common.c
index 96828d59..087465b5 100644
--- a/src/devices/wifi/nm-wifi-common.c
+++ b/src/devices/wifi/nm-wifi-common.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/devices/wifi/nm-wifi-common.h b/src/devices/wifi/nm-wifi-common.h
index 81d657ec..829df826 100644
--- a/src/devices/wifi/nm-wifi-common.h
+++ b/src/devices/wifi/nm-wifi-common.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_WIFI_COMMON_H__
diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c
index 2f069882..821460a5 100644
--- a/src/devices/wifi/nm-wifi-factory.c
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 - 2014 Red Hat, Inc.
  */
 
diff --git a/src/devices/wifi/nm-wifi-p2p-peer.c b/src/devices/wifi/nm-wifi-p2p-peer.c
index 4b524623..f8da0046 100644
--- a/src/devices/wifi/nm-wifi-p2p-peer.c
+++ b/src/devices/wifi/nm-wifi-p2p-peer.c
@@ -1,20 +1,5 @@
-/* NetworkManager -- Wi-Fi P2P Peer
- *
- * 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/devices/wifi/nm-wifi-p2p-peer.h b/src/devices/wifi/nm-wifi-p2p-peer.h
index d6ff7abc..07f25cc1 100644
--- a/src/devices/wifi/nm-wifi-p2p-peer.h
+++ b/src/devices/wifi/nm-wifi-p2p-peer.h
@@ -1,20 +1,5 @@
-/* NetworkManager -- Wi-Fi P2P Peer
- *
- * 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c
index 426eeea8..b9b7ec42 100644
--- a/src/devices/wifi/nm-wifi-utils.c
+++ b/src/devices/wifi/nm-wifi-utils.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2011 Red Hat, Inc.
+ * Copyright (C) 2011 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -297,96 +283,79 @@ verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
                 guint32 rsn_flags,
                 GError **error)
 {
-	const char *key_mgmt, *auth_alg, *tmp;
-	int n;
+	const char *key_mgmt, *auth_alg;
 
 	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
 	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
 
-	if (key_mgmt) {
-		if (!strcmp (key_mgmt, "wpa-psk") || !strcmp (key_mgmt, "wpa-none")) {
-			if (s_8021x) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_SETTING,
-				                     _("WPA-PSK authentication is incompatible with 802.1x"));
-				g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
-				return FALSE;
-			}
+	if (!nm_streq0 (key_mgmt, "wpa-psk"))
+		return TRUE;
 
-			if (auth_alg && strcmp (auth_alg, "open")) {
-				/* WPA must use "open" authentication */
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     _("WPA-PSK requires 'open' authentication"));
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-				                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
-				return FALSE;
-			}
-		}
+	if (s_8021x) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_SETTING,
+		                     _("WPA-PSK authentication is incompatible with 802.1x"));
+		g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
+		return FALSE;
+	}
 
-		if (!strcmp (key_mgmt, "wpa-none")) {
-			if (!adhoc) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     _("WPA Ad-Hoc authentication requires an Ad-Hoc mode AP"));
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME,
-				                NM_SETTING_WIRELESS_MODE);
-				return FALSE;
-			}
+	if (auth_alg && !nm_streq (auth_alg, "open")) {
+		/* WPA must use "open" authentication */
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("WPA-PSK requires 'open' authentication"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+		return FALSE;
+	}
 
-			/* Ad-Hoc WPA requires 'wpa' proto, 'none' pairwise, and 'tkip' group */
-			n = nm_setting_wireless_security_get_num_protos (s_wsec);
-			tmp = (n > 0) ? nm_setting_wireless_security_get_proto (s_wsec, 0) : NULL;
-			if (n > 1 || !tmp || strcmp (tmp, "wpa")) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     _("WPA Ad-Hoc authentication requires 'wpa' protocol"));
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-				                NM_SETTING_WIRELESS_SECURITY_PROTO);
-				return FALSE;
-			}
+	/* Make sure the AP's capabilities support WPA-PSK */
+	if (   !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+	    && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("Access point does not support PSK but setting requires it"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+		return FALSE;
+	}
 
-			n = nm_setting_wireless_security_get_num_pairwise (s_wsec);
-			tmp = (n > 0) ? nm_setting_wireless_security_get_pairwise (s_wsec, 0) : NULL;
-			if (n > 1 || g_strcmp0 (tmp, "none")) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     _("WPA Ad-Hoc authentication requires 'none' pairwise cipher"));
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-				                NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
-				return FALSE;
-			}
+	if (adhoc) {
+		/* Ad-Hoc RSN requires 'rsn' proto, 'ccmp' pairwise, and 'ccmp' group */
+		if (   nm_setting_wireless_security_get_num_protos (s_wsec) != 1
+		    || !nm_streq0 (nm_setting_wireless_security_get_proto (s_wsec, 0), "rsn")) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("WPA Ad-Hoc authentication requires 'rsn' protocol"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_PROTO);
+			return FALSE;
+		}
 
-			n = nm_setting_wireless_security_get_num_groups (s_wsec);
-			tmp = (n > 0) ? nm_setting_wireless_security_get_group (s_wsec, 0) : NULL;
-			if (n > 1 || !tmp || strcmp (tmp, "tkip")) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     _("WPA Ad-Hoc requires 'tkip' group cipher"));
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-				                NM_SETTING_WIRELESS_SECURITY_GROUP);
-				return FALSE;
-			}
+		if (   nm_setting_wireless_security_get_num_pairwise (s_wsec) != 1
+		    || !nm_streq0 (nm_setting_wireless_security_get_pairwise (s_wsec, 0), "ccmp")) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("WPA Ad-Hoc authentication requires 'ccmp' pairwise cipher"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+			return FALSE;
 		}
 
-		if (!strcmp (key_mgmt, "wpa-psk")) {
-			/* Make sure the AP's capabilities support WPA-PSK */
-			if (   !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
-			    && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     _("Access point does not support PSK but setting requires it"));
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-				                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
-				return FALSE;
-			}
+		if (   nm_setting_wireless_security_get_num_groups (s_wsec) != 1
+		    || !nm_streq0 (nm_setting_wireless_security_get_group (s_wsec, 0), "ccmp")) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("WPA Ad-Hoc requires 'ccmp' group cipher"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_GROUP);
+			return FALSE;
 		}
 	}
 
@@ -463,61 +432,52 @@ verify_adhoc (NMSettingWirelessSecurity *s_wsec,
 {
 	const char *key_mgmt = NULL, *leap_username = NULL, *auth_alg = NULL;
 
+	if (!adhoc)
+		return TRUE;
+
 	if (s_wsec) {
 		key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
 		auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
 		leap_username = nm_setting_wireless_security_get_leap_username (s_wsec);
 	}
 
-	if (adhoc) {
-		if (key_mgmt && strcmp (key_mgmt, "wpa-none") && strcmp (key_mgmt, "none")) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("Access point mode is Ad-Hoc but setting requires Infrastructure security"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-			                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
-			return FALSE;
-		}
+	if (key_mgmt && !NM_IN_STRSET (key_mgmt, "none", "wpa-psk")) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("Ad-Hoc mode requires 'none' or 'wpa-psk' key management"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+		return FALSE;
+	}
 
-		if (s_8021x) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_SETTING,
-			                     _("Ad-Hoc mode is incompatible with 802.1x security"));
-			g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
-			return FALSE;
-		}
+	if (s_8021x) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_SETTING,
+		                     _("Ad-Hoc mode is incompatible with 802.1x security"));
+		g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
+		return FALSE;
+	}
 
-		if (leap_username) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("Ad-Hoc mode is incompatible with LEAP security"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-			                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
-			return FALSE;
-		}
+	if (leap_username) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("Ad-Hoc mode is incompatible with LEAP security"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+		return FALSE;
+	}
 
-		if (auth_alg && strcmp (auth_alg, "open")) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("Ad-Hoc mode requires 'open' authentication"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-			                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
-			return FALSE;
-		}
-	} else {
-		if (key_mgmt && !strcmp (key_mgmt, "wpa-none")) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("Access point mode is Infrastructure but setting requires Ad-Hoc security"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-			                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
-			return FALSE;
-		}
+	if (auth_alg && !nm_streq (auth_alg, "open")) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("Ad-Hoc mode requires 'open' authentication"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+		return FALSE;
 	}
 
 	return TRUE;
@@ -773,11 +733,13 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid,
 		return FALSE;
 
 	if (adhoc) {
-		g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL);
-		/* Ad-Hoc does not support RSN/WPA2 */
-		nm_setting_wireless_security_add_proto (s_wsec, "wpa");
-		nm_setting_wireless_security_add_pairwise (s_wsec, "none");
-		nm_setting_wireless_security_add_group (s_wsec, "tkip");
+		g_object_set (s_wsec,
+		              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+		              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+		              NULL);
+		nm_setting_wireless_security_add_proto (s_wsec, "rsn");
+		nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp");
+		nm_setting_wireless_security_add_group (s_wsec, "ccmp");
 	} else if (s_8021x) {
 		g_object_set (s_wsec,
 		              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h
index 251d122f..982080b9 100644
--- a/src/devices/wifi/nm-wifi-utils.h
+++ b/src/devices/wifi/nm-wifi-utils.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2011 Red Hat, Inc.
+ * Copyright (C) 2011 Red Hat, Inc.
  */
 
 #ifndef __NM_WIFI_UTILS_H__
diff --git a/src/devices/wifi/tests/meson.build b/src/devices/wifi/tests/meson.build
deleted file mode 100644
index ba756d53..00000000
--- a/src/devices/wifi/tests/meson.build
+++ /dev/null
@@ -1,14 +0,0 @@
-test_unit = 'test-devices-wifi'
-
-exe = executable(
-  test_unit,
-  [test_unit + '.c'] + common_sources,
-  dependencies: test_nm_dep,
-)
-
-test(
-  test_unit,
-  test_script,
-  args: test_args + [exe.full_path()],
-  timeout: default_test_timeout,
-)
diff --git a/src/devices/wifi/tests/test-devices-wifi.c b/src/devices/wifi/tests/test-devices-wifi.c
index a0b3e17f..a960e7a2 100644
--- a/src/devices/wifi/tests/test-devices-wifi.c
+++ b/src/devices/wifi/tests/test-devices-wifi.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2011 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index 7ccebcb5..c368a590 100644
--- a/src/devices/wwan/libnm-wwan.ver
+++ b/src/devices/wwan/libnm-wwan.ver
@@ -3,6 +3,7 @@ global:
 	nm_modem_act_stage1_prepare;
 	nm_modem_act_stage2_config;
 	nm_modem_check_connection_compatible;
+	nm_modem_claim;
 	nm_modem_complete_connection;
 	nm_modem_deactivate;
 	nm_modem_deactivate_async;
@@ -14,15 +15,17 @@ global:
 	nm_modem_get_device_id;
 	nm_modem_get_driver;
 	nm_modem_get_iid;
-	nm_modem_get_path;
 	nm_modem_get_ip_ifindex;
 	nm_modem_get_operator_code;
+	nm_modem_get_path;
 	nm_modem_get_secrets;
 	nm_modem_get_state;
 	nm_modem_get_type;
 	nm_modem_get_uid;
 	nm_modem_ip4_pre_commit;
+	nm_modem_is_claimed;
 	nm_modem_manager_get;
+	nm_modem_manager_get_modems;
 	nm_modem_manager_get_type;
 	nm_modem_manager_name_owner_get;
 	nm_modem_manager_name_owner_ref;
@@ -32,6 +35,7 @@ global:
 	nm_modem_stage3_ip4_config_start;
 	nm_modem_stage3_ip6_config_start;
 	nm_modem_state_to_string;
+	nm_modem_unclaim;
 local:
 	*;
 };
diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build
index 482dc205..ed6f8010 100644
--- a/src/devices/wwan/meson.build
+++ b/src/devices/wwan/meson.build
@@ -1,13 +1,15 @@
-sources = files(
+nm_service_providers_source = files('nm-service-providers.c')
+
+sources = nm_service_providers_source + files(
   'nm-modem-broadband.c',
   'nm-modem.c',
   'nm-modem-manager.c',
 )
 
 deps = [
+  daemon_nm_default_dep,
   libsystemd_dep,
   mm_glib_dep,
-  nm_dep,
 ]
 
 if enable_ofono
@@ -20,16 +22,17 @@ libnm_wwan = shared_module(
   'nm-wwan',
   sources: sources,
   dependencies: deps,
-  link_args: [
-    '-Wl,--version-script,@0@'.format(linker_script),
-  ],
+  c_args: daemon_c_flags,
+  link_args: '-Wl,--version-script,@0@'.format(linker_script),
   link_depends: linker_script,
   install: true,
   install_dir: nm_plugindir,
 )
 
+wwan_inc = include_directories('.')
+
 libnm_wwan_dep = declare_dependency(
-  include_directories: include_directories('.'),
+  include_directories: wwan_inc,
   link_with: libnm_wwan,
 )
 
@@ -50,6 +53,7 @@ libnm_device_plugin_wwan = shared_module(
   'nm-device-plugin-wwan',
   sources: sources,
   dependencies: deps,
+  c_args: daemon_c_flags,
   link_with: libnm_wwan,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
@@ -66,11 +70,21 @@ run_target(
   depends: libnm_device_plugin_wwan,
 )
 
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-wwan: src/devices/wwan/libnm-device-plugin-wwan.la src/devices/wwan/libnm-wwan.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so)
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-wwan.so "$(srcdir)/src/devices/wwan/libnm-wwan.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-wwan.so)
-'''
+if enable_tests
+  test_unit = 'test-service-providers'
+
+  exe = executable(
+    test_unit,
+    ['tests/' + test_unit + '.c'] + nm_service_providers_source,
+    include_directories: wwan_inc,
+    dependencies: libnetwork_manager_test_dep,
+    c_args: test_c_flags,
+  )
+
+  test(
+    'wwan/' + test_unit,
+    test_script,
+    timeout: default_test_timeout,
+    args: test_args + [exe.full_path()],
+  )
+endif
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 042a6ca4..3e6ccc8f 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2019 Red Hat, Inc.
  */
 
@@ -48,10 +34,11 @@ typedef struct {
 	NMModem *modem;
 	NMDeviceModemCapabilities caps;
 	NMDeviceModemCapabilities current_caps;
-	gboolean rf_enabled;
 	char *device_id;
 	char *operator_code;
 	char *apn;
+	bool rf_enabled:1;
+	NMDeviceStageState stage1_state:3;
 } NMDeviceModemPrivate;
 
 struct _NMDeviceModem {
@@ -119,16 +106,17 @@ modem_prepare_result (NMModem *modem,
                       gpointer user_data)
 {
 	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
-	NMDeviceState state;
 	NMDeviceStateReason reason = i_reason;
 
-	state = nm_device_get_state (device);
-	g_return_if_fail (state == NM_DEVICE_STATE_PREPARE);
+	if (   nm_device_get_state (device) != NM_DEVICE_STATE_PREPARE
+	    || priv->stage1_state != NM_DEVICE_STAGE_STATE_PENDING) {
+		nm_assert_not_reached ();
+		success = FALSE;
+	}
 
-	if (success)
-		nm_device_activate_schedule_stage2_device_config (device);
-	else {
+	if (!success) {
 		/* There are several reasons to block autoconnection at device level:
 		 *
 		 *  - Wrong SIM-PIN: The device won't autoconnect because it doesn't make sense
@@ -164,7 +152,11 @@ modem_prepare_result (NMModem *modem,
 			break;
 		}
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		return;
 	}
+
+	priv->stage1_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+	nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 static void
@@ -187,16 +179,19 @@ static void
 modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+
+	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
 
 	if (error) {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-	} else {
-		/* Otherwise, on success for modem secrets we need to schedule stage1 again */
-		g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
-		nm_device_activate_schedule_stage1_device_prepare (device);
+		return;
 	}
+
+	priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT;
+	nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 static void
@@ -355,7 +350,7 @@ modem_state_cb (NMModem *modem,
 	NMModemState new_state = new_state_i;
 	NMModemState old_state = old_state_i;
 	NMDevice *device = NM_DEVICE (user_data);
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 	NMDeviceState dev_state = nm_device_get_state (device);
 
 	if (new_state <= NM_MODEM_STATE_DISABLING &&
@@ -409,7 +404,7 @@ modem_removed_cb (NMModem *modem, gpointer user_data)
 static gboolean
 owns_iface (NMDevice *device, const char *iface)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 
 	g_return_val_if_fail (priv->modem, FALSE);
 
@@ -447,7 +442,7 @@ get_generic_capabilities (NMDevice *device)
 static const char *
 get_type_description (NMDevice *device)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 
 	if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS))
 		return "gsm";
@@ -464,7 +459,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError
 	if (!NM_DEVICE_CLASS (nm_device_modem_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
-	if (!nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem,
+	if (!nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
 	                                           connection,
 	                                           error ? &local : NULL)) {
 		if (error) {
@@ -530,7 +525,7 @@ complete_connection (NMDevice *device,
                      NMConnection *const*existing_connections,
                      GError **error)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 
 	return nm_modem_complete_connection (priv->modem,
 	                                     nm_device_get_iface (device),
@@ -542,7 +537,10 @@ complete_connection (NMDevice *device,
 static void
 deactivate (NMDevice *device)
 {
-	nm_modem_deactivate (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+
+	nm_modem_deactivate (priv->modem, device);
+	priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT;
 }
 
 /*****************************************************************************/
@@ -583,28 +581,32 @@ deactivate_async (NMDevice *self,
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
-	NMActStageReturn ret;
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 	NMActRequest *req;
 
-	ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
 	req = nm_device_get_act_request (device);
 	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
-	return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, out_failure_reason);
+	if (priv->stage1_state == NM_DEVICE_STAGE_STATE_INIT) {
+		priv->stage1_state = NM_DEVICE_STAGE_STATE_PENDING;
+		return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
+		                                    req,
+		                                    out_failure_reason);
+	}
+
+	if (priv->stage1_state == NM_DEVICE_STAGE_STATE_PENDING)
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+
+	nm_assert (priv->stage1_state == NM_DEVICE_STAGE_STATE_COMPLETED);
+	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 static NMActStageReturn
 act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
-	NMActRequest *req;
-
-	req = nm_device_get_act_request (device);
-	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+	nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem);
 
-	return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, out_failure_reason);
+	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 static NMActStageReturn
@@ -632,7 +634,7 @@ act_stage3_ip_config_start (NMDevice *device,
 static void
 ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 {
-	nm_modem_ip4_pre_commit (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, device, config);
+	nm_modem_ip4_pre_commit (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device, config);
 }
 
 static gboolean
@@ -654,7 +656,7 @@ get_ip_iface_identifier (NMDevice *device, NMUtilsIPv6IfaceId *out_iid)
 static gboolean
 get_enabled (NMDevice *device)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 	NMModemState modem_state = nm_modem_get_state (priv->modem);
 
 	return priv->rf_enabled && (modem_state >= NM_MODEM_STATE_LOCKED);
@@ -710,7 +712,7 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 
 	g_return_if_fail (modem != NULL);
 
-	priv->modem = g_object_ref (modem);
+	priv->modem = nm_modem_claim (modem);
 
 	g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
 	g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
@@ -745,7 +747,7 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_MODEM:
@@ -776,7 +778,7 @@ static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_MODEM:
@@ -834,11 +836,11 @@ nm_device_modem_new (NMModem *modem)
 static void
 dispose (GObject *object)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
 
 	if (priv->modem) {
 		g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object));
-		g_clear_object (&priv->modem);
+		nm_clear_pointer (&priv->modem, nm_modem_unclaim);
 	}
 
 	g_clear_pointer (&priv->device_id, g_free);
diff --git a/src/devices/wwan/nm-device-modem.h b/src/devices/wwan/nm-device-modem.h
index 0a557f57..842096e2 100644
--- a/src/devices/wwan/nm-device-modem.h
+++ b/src/devices/wwan/nm-device-modem.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Red Hat, Inc.
  */
 
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 216fedfe..a1ae8671 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -1,25 +1,12 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
  */
 
 #include "nm-default.h"
 
 #include "nm-modem-broadband.h"
+#include "nm-service-providers.h"
 
 #include <arpa/inet.h>
 #include <libmm-glib.h>
@@ -274,7 +261,10 @@ create_cdma_connect_properties (NMConnection *connection)
 }
 
 static MMSimpleConnectProperties *
-create_gsm_connect_properties (NMConnection *connection)
+create_gsm_connect_properties (NMConnection *connection,
+                               const char *apn,
+                               const char *username,
+                               const char *password)
 {
 	NMSettingGsm *setting;
 	NMSettingPpp *s_ppp;
@@ -282,11 +272,14 @@ create_gsm_connect_properties (NMConnection *connection)
 	const char *str;
 
 	setting = nm_connection_get_setting_gsm (connection);
+
 	properties = mm_simple_connect_properties_new ();
 
-	/* Blank APN ("") means the default subscription APN */
-	str = nm_setting_gsm_get_apn (setting);
-	mm_simple_connect_properties_set_apn (properties, str ?: "");
+	mm_simple_connect_properties_set_apn (properties, apn ?: "");
+	if (username)
+		mm_simple_connect_properties_set_user (properties, username);
+	if (password)
+		mm_simple_connect_properties_set_password (properties, password);
 
 	str = nm_setting_gsm_get_network_id (setting);
 	if (str)
@@ -296,14 +289,6 @@ create_gsm_connect_properties (NMConnection *connection)
 	if (str)
 		mm_simple_connect_properties_set_pin (properties, str);
 
-	str = nm_setting_gsm_get_username (setting);
-	if (str)
-		mm_simple_connect_properties_set_user (properties, str);
-
-	str = nm_setting_gsm_get_password (setting);
-	if (str)
-		mm_simple_connect_properties_set_password (properties, str);
-
 	/* Roaming */
 	if (nm_setting_gsm_get_home_only (setting))
 		mm_simple_connect_properties_set_allow_roaming (properties, FALSE);
@@ -356,16 +341,26 @@ connect_ready (MMModemSimple *simple_iface,
                GAsyncResult *res,
                NMModemBroadband *self)
 {
-	ConnectContext *ctx = self->_priv.ctx;
+	ConnectContext *ctx;
 	GError *error = NULL;
 	NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
 	NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
+	MMBearer *bearer;
 
-	self->_priv.bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
+	bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_error_free (error);
+		return;
+	}
+
+	ctx = self->_priv.ctx;
 
 	if (!ctx)
 		return;
 
+	self->_priv.bearer = bearer;
+
 	if (!self->_priv.bearer) {
 		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
 		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
@@ -457,6 +452,95 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
 }
 
 static void
+find_gsm_apn_cb (const char *apn,
+                 const char *username,
+                 const char *password,
+                 const char *gateway,
+                 const char *auth_method,
+                 const GSList *dns,
+                 GError *error,
+                 gpointer user_data)
+{
+	NMModemBroadband *self = user_data;
+	NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self);
+	ConnectContext *ctx = priv->ctx;
+
+	if (error) {
+		_LOGW ("failed to connect '%s': APN not found: %s",
+		       nm_connection_get_id (ctx->connection), error->message);
+
+		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_GSM_APN_FAILED);
+		connect_context_clear (self);
+		return;
+	}
+
+	/* Blank APN ("") means the default subscription APN */
+	ctx->connect_properties = create_gsm_connect_properties (ctx->connection,
+	                                                         apn,
+	                                                         username,
+	                                                         password);
+	g_return_if_fail (ctx->connect_properties);
+	connect_context_step (self);
+}
+
+static gboolean
+try_create_connect_properties (NMModemBroadband *self)
+{
+	NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self);
+	ConnectContext *ctx = priv->ctx;
+
+	if (MODEM_CAPS_3GPP (ctx->caps)) {
+		NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection);
+
+		if (!s_gsm || nm_setting_gsm_get_auto_config (s_gsm)) {
+			gs_unref_object MMModem3gpp *modem_3gpp = NULL;
+			const char *network_id = NULL;
+
+			s_gsm = nm_connection_get_setting_gsm (ctx->connection);
+			if (s_gsm)
+				network_id = nm_setting_gsm_get_network_id (s_gsm);
+			if (!network_id) {
+				if (mm_modem_get_state (self->_priv.modem_iface) < MM_MODEM_STATE_REGISTERED)
+					return FALSE;
+				modem_3gpp = mm_object_get_modem_3gpp (priv->modem_object);
+				network_id = mm_modem_3gpp_get_operator_code (modem_3gpp);
+			}
+			if (!network_id) {
+				_LOGW ("failed to connect '%s': unable to determine the network id",
+				       nm_connection_get_id (ctx->connection));
+				goto out;
+			}
+
+			nm_service_providers_find_gsm_apn (MOBILE_BROADBAND_PROVIDER_INFO_DATABASE,
+			                                   network_id,
+			                                   ctx->cancellable,
+			                                   find_gsm_apn_cb,
+			                                   self);
+		} else {
+			ctx->connect_properties = create_gsm_connect_properties (ctx->connection,
+			                                                         nm_setting_gsm_get_apn (s_gsm),
+			                                                         nm_setting_gsm_get_username (s_gsm),
+			                                                         nm_setting_gsm_get_password (s_gsm));
+			g_return_val_if_fail (ctx->connect_properties, TRUE);
+		}
+
+		return TRUE;
+	} else if (MODEM_CAPS_3GPP2 (ctx->caps)) {
+		ctx->connect_properties = create_cdma_connect_properties (ctx->connection);
+		g_return_val_if_fail (ctx->connect_properties, FALSE);
+		return TRUE;
+	} else {
+		_LOGW ("failed to connect '%s': not a mobile broadband modem",
+		       nm_connection_get_id (ctx->connection));
+	}
+
+out:
+	nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+	connect_context_clear (self);
+	return TRUE;
+}
+
+static void
 connect_context_step (NMModemBroadband *self)
 {
 	ConnectContext *ctx = self->_priv.ctx;
@@ -500,22 +584,11 @@ connect_context_step (NMModemBroadband *self)
 		if (mm_modem_get_state (self->_priv.modem_iface) <= MM_MODEM_STATE_LOCKED)
 			break;
 
-		/* Create core connect properties based on the modem capabilities */
-		g_assert (!ctx->connect_properties);
-
-		if (MODEM_CAPS_3GPP (ctx->caps))
-			ctx->connect_properties = create_gsm_connect_properties (ctx->connection);
-		else if (MODEM_CAPS_3GPP2 (ctx->caps))
-			ctx->connect_properties = create_cdma_connect_properties (ctx->connection);
-		else {
-			_LOGW ("failed to connect '%s': not a mobile broadband modem",
-			       nm_connection_get_id (ctx->connection));
+		if (!try_create_connect_properties (self))
+			break;
 
-			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
-			connect_context_clear (self);
+		if (!self->_priv.ctx)
 			break;
-		}
-		g_assert (ctx->connect_properties);
 
 		/* Build up list of IP types that we need to use in the retries */
 		ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error);
@@ -534,6 +607,9 @@ connect_context_step (NMModemBroadband *self)
 	}
 		/* fall through */
 	case CONNECT_STEP_CONNECT:
+		if (!ctx->connect_properties)
+			break;
+
 		if (ctx->ip_types_i < ctx->ip_types->len) {
 			NMModemIPType current;
 
@@ -546,7 +622,7 @@ connect_context_step (NMModemBroadband *self)
 			else if (current == NM_MODEM_IP_TYPE_IPV4V6)
 				mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6);
 			else
-				g_assert_not_reached ();
+				g_return_if_reached ();
 
 			_nm_modem_set_apn (NM_MODEM (self), mm_simple_connect_properties_get_apn (ctx->connect_properties));
 
@@ -556,7 +632,7 @@ connect_context_step (NMModemBroadband *self)
 
 			mm_modem_simple_connect (self->_priv.simple_iface,
 			                         ctx->connect_properties,
-			                         NULL,
+			                         ctx->cancellable,
 			                         (GAsyncReadyCallback) connect_ready,
 			                         self);
 			break;
@@ -586,9 +662,9 @@ connect_context_step (NMModemBroadband *self)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMModem *_self,
-                    NMConnection *connection,
-                    NMDeviceStateReason *out_failure_reason)
+modem_act_stage1_prepare (NMModem *_self,
+                          NMConnection *connection,
+                          NMDeviceStateReason *out_failure_reason)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 
@@ -689,6 +765,9 @@ complete_connection (NMModem *modem,
 		if (!s_gsm) {
 			s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
 			nm_connection_add_setting (connection, NM_SETTING (s_gsm));
+			g_object_set (G_OBJECT (s_gsm),
+			              NM_SETTING_GSM_AUTO_CONFIG, TRUE,
+			              NULL);
 		}
 
 		if (!nm_setting_gsm_get_device_id (s_gsm)) {
@@ -876,8 +955,8 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	guint32 ip4_route_table, ip4_route_metric;
 	NMPlatformIP4Route *r;
 
-	g_assert (self->_priv.ipv4_config);
-	g_assert (self->_priv.bearer);
+	g_return_val_if_fail (self->_priv.ipv4_config, FALSE);
+	g_return_val_if_fail (self->_priv.bearer, FALSE);
 
 	self->_priv.idle_id_ip4 = 0;
 
@@ -908,7 +987,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	}
 
 	data_port = mm_bearer_get_interface (self->_priv.bearer);
-	g_assert (data_port);
+	g_return_val_if_fail (data_port, FALSE);
 	config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
 	                            nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
 
@@ -983,7 +1062,7 @@ stage3_ip6_done (NMModemBroadband *self)
 	const char **dns;
 	guint i;
 
-	g_assert (self->_priv.ipv6_config);
+	g_return_val_if_fail (self->_priv.ipv6_config, FALSE);
 
 	self->_priv.idle_id_ip6 = 0;
 	memset (&address, 0, sizeof (address));
@@ -1015,7 +1094,8 @@ stage3_ip6_done (NMModemBroadband *self)
 	_LOGI ("IPv6 base configuration:");
 
 	data_port = mm_bearer_get_interface (self->_priv.bearer);
-	g_assert (data_port);
+	g_return_val_if_fail (data_port, FALSE);
+
 	config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
 	                            nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
 
@@ -1383,8 +1463,9 @@ set_property (GObject *object,
 		/* construct-only */
 		self->_priv.modem_object = g_value_dup_object (value);
 		self->_priv.modem_iface = mm_object_get_modem (self->_priv.modem_object);
+		g_return_if_fail (self->_priv.modem_iface);
 		self->_priv.modem_3gpp_iface = mm_object_get_modem_3gpp (self->_priv.modem_object);
-		g_assert (self->_priv.modem_iface != NULL);
+
 		g_signal_connect (self->_priv.modem_iface,
 		                  "state-changed",
 		                  G_CALLBACK (modem_state_changed),
@@ -1512,7 +1593,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
 	modem_class->get_user_pass = get_user_pass;
 	modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem;
 	modem_class->complete_connection = complete_connection;
-	modem_class->act_stage1_prepare = act_stage1_prepare;
+	modem_class->modem_act_stage1_prepare = modem_act_stage1_prepare;
 	modem_class->owns_port = owns_port;
 
 	obj_properties[PROP_MODEM] =
diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h
index 9404f0b9..43041e2b 100644
--- a/src/devices/wwan/nm-modem-broadband.h
+++ b/src/devices/wwan/nm-modem-broadband.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2012 - Aleksander Morgado <aleksander@gnu.org>
  */
 
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index db2c0192..daccf68a 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2014 Red Hat, Inc.
  * Copyright (C) 2009 Novell, Inc.
  * Copyright (C) 2009 - 2013 Canonical Ltd.
@@ -149,6 +135,20 @@ remove_one_modem (gpointer key, gpointer value, gpointer user_data)
 
 /*****************************************************************************/
 
+NMModem **
+nm_modem_manager_get_modems (NMModemManager *self,
+                             guint *out_len)
+{
+	g_return_val_if_fail (NM_IS_MODEM_MANAGER (self), NULL);
+
+	return (NMModem **) nm_utils_hash_values_to_array (NM_MODEM_MANAGER_GET_PRIVATE (self)->modems,
+	                                                   NULL,
+	                                                   NULL,
+	                                                   out_len);
+}
+
+/*****************************************************************************/
+
 static void
 modm_clear_manager (NMModemManager *self)
 {
diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h
index 1a26fd9f..97a2126a 100644
--- a/src/devices/wwan/nm-modem-manager.h
+++ b/src/devices/wwan/nm-modem-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2014 Red Hat, Inc.
  * Copyright (C) 2009 Novell, Inc.
  * Copyright (C) 2009 Canonical Ltd.
@@ -51,4 +37,7 @@ void nm_modem_manager_name_owner_unref (NMModemManager *self);
 
 const char *nm_modem_manager_name_owner_get (NMModemManager *self);
 
+NMModem **nm_modem_manager_get_modems (NMModemManager *self,
+                                       guint *out_len);
+
 #endif /* __NETWORKMANAGER_MODEM_MANAGER_H__ */
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index 31111b62..b68cd7e8 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 - 2016 Canonical Ltd.
  */
 
@@ -1114,9 +1100,9 @@ create_connect_properties (NMConnection *connection)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMModem *modem,
-                    NMConnection *connection,
-                    NMDeviceStateReason *out_failure_reason)
+modem_act_stage1_prepare (NMModem *modem,
+                          NMConnection *connection,
+                          NMDeviceStateReason *out_failure_reason)
 {
 	NMModemOfono *self = NM_MODEM_OFONO (modem);
 	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
@@ -1308,6 +1294,6 @@ nm_modem_ofono_class_init (NMModemOfonoClass *klass)
 	modem_class->deactivate_cleanup = deactivate_cleanup;
 	modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem;
 
-	modem_class->act_stage1_prepare = act_stage1_prepare;
+	modem_class->modem_act_stage1_prepare = modem_act_stage1_prepare;
 	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
index 1dcd79b0..4ff2fbdd 100644
--- a/src/devices/wwan/nm-modem-ofono.h
+++ b/src/devices/wwan/nm-modem-ofono.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 - Canonical Ltd.
  */
 
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index fb2316d5..ad6449b1 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2014 Red Hat, Inc.
  * Copyright (C) 2009 Novell, Inc.
  */
@@ -110,6 +96,8 @@ typedef struct _NMModemPrivate {
 	/* PPP stats */
 	guint32 in_bytes;
 	guint32 out_bytes;
+
+	bool claimed:1;
 } NMModemPrivate;
 
 G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT)
@@ -187,6 +175,53 @@ nm_modem_state_to_string (NMModemState state)
 	return NULL;
 }
 
+/*****************************************************************************/
+
+gboolean
+nm_modem_is_claimed (NMModem *self)
+{
+	g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
+
+	return NM_MODEM_GET_PRIVATE (self)->claimed;
+}
+
+NMModem *
+nm_modem_claim (NMModem *self)
+{
+	NMModemPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+
+	g_return_val_if_fail (!priv->claimed, NULL);
+
+	priv->claimed = TRUE;
+	return g_object_ref (self);
+}
+
+void
+nm_modem_unclaim (NMModem *self)
+{
+	NMModemPrivate *priv;
+
+	g_return_if_fail (NM_IS_MODEM (self));
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->claimed);
+
+	/* we don't actually unclaim the instance. This instance should not be re-used
+	 * by another owner, that is because we only claim modems as we receive them.
+	 * There is no mechanism that somebody else would later re-use them again.
+	 *
+	 * // priv->claimed = FALSE; */
+
+	g_object_unref (self);
+}
+
+/*****************************************************************************/
+
 NMModemState
 nm_modem_get_state (NMModem *self)
 {
@@ -976,9 +1011,9 @@ nm_modem_get_secrets (NMModem *self,
 /*****************************************************************************/
 
 static NMActStageReturn
-act_stage1_prepare (NMModem *modem,
-                    NMConnection *connection,
-                    NMDeviceStateReason *out_failure_reason)
+modem_act_stage1_prepare (NMModem *modem,
+                          NMConnection *connection,
+                          NMDeviceStateReason *out_failure_reason)
 {
 	NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_UNKNOWN);
 	return NM_ACT_STAGE_RETURN_FAILURE;
@@ -995,6 +1030,8 @@ nm_modem_act_stage1_prepare (NMModem *self,
 	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 	NMConnection *connection;
 
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+
 	if (priv->act_request)
 		g_object_unref (priv->act_request);
 	priv->act_request = g_object_ref (req);
@@ -1005,7 +1042,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
 	setting_name = nm_connection_need_secrets (connection, &hints);
 	if (!setting_name) {
 		nm_assert (!hints);
-		return NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, connection, out_failure_reason);
+		return NM_MODEM_GET_CLASS (self)->modem_act_stage1_prepare (self, connection, out_failure_reason);
 	}
 
 	/* Secrets required... */
@@ -1029,19 +1066,18 @@ nm_modem_act_stage1_prepare (NMModem *self,
 
 /*****************************************************************************/
 
-NMActStageReturn
-nm_modem_act_stage2_config (NMModem *self,
-                            NMActRequest *req,
-                            NMDeviceStateReason *out_failure_reason)
+void
+nm_modem_act_stage2_config (NMModem *self)
 {
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMModemPrivate *priv;
+
+	g_return_if_fail (NM_IS_MODEM (self));
 
+	priv = NM_MODEM_GET_PRIVATE (self);
 	/* Clear secrets tries counter since secrets were successfully used
 	 * already if we get here.
 	 */
 	priv->secrets_tries = 0;
-
-	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 /*****************************************************************************/
@@ -1800,7 +1836,7 @@ nm_modem_class_init (NMModemClass *klass)
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
-	klass->act_stage1_prepare = act_stage1_prepare;
+	klass->modem_act_stage1_prepare = modem_act_stage1_prepare;
 	klass->stage3_ip6_config_request = stage3_ip6_config_request;
 	klass->deactivate_cleanup = deactivate_cleanup;
 
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index fccb4fec..f2de990b 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2011 Red Hat, Inc.
  * Copyright (C) 2009 Novell, Inc.
  */
@@ -136,9 +122,9 @@ typedef struct {
 	                                            NMConnection *const*existing_connections,
 	                                            GError **error);
 
-	NMActStageReturn (*act_stage1_prepare)     (NMModem *modem,
-	                                            NMConnection *connection,
-	                                            NMDeviceStateReason *out_failure_reason);
+	NMActStageReturn (*modem_act_stage1_prepare) (NMModem *modem,
+	                                              NMConnection *connection,
+	                                              NMDeviceStateReason *out_failure_reason);
 
 	NMActStageReturn (*static_stage3_ip4_config_start) (NMModem *self,
 	                                                    NMActRequest *req,
@@ -167,6 +153,10 @@ typedef struct {
 
 GType nm_modem_get_type (void);
 
+gboolean nm_modem_is_claimed (NMModem *modem);
+NMModem *nm_modem_claim (NMModem *modem);
+void nm_modem_unclaim (NMModem *modem);
+
 const char *nm_modem_get_path            (NMModem *modem);
 const char *nm_modem_get_uid             (NMModem *modem);
 const char *nm_modem_get_control_port    (NMModem *modem);
@@ -222,9 +212,7 @@ NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem,
                                               NMActRequest *req,
                                               NMDeviceStateReason *out_failure_reason);
 
-NMActStageReturn nm_modem_act_stage2_config (NMModem *modem,
-                                             NMActRequest *req,
-                                             NMDeviceStateReason *out_failure_reason);
+void nm_modem_act_stage2_config (NMModem *modem);
 
 NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem,
                                                    NMDevice *device,
diff --git a/src/devices/wwan/nm-service-providers.c b/src/devices/wwan/nm-service-providers.c
new file mode 100644
index 00000000..5140f7ed
--- /dev/null
+++ b/src/devices/wwan/nm-service-providers.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2009 Novell, Inc.
+ * Author: Tambet Ingo (tambet@gmail.com).
+ * Copyright (C) 2009 - 2019 Red Hat, Inc.
+ * Copyright (C) 2012 Lanedo GmbH
+ */
+
+#include "nm-default.h"
+
+#include "nm-service-providers.h"
+
+typedef enum {
+	PARSER_TOPLEVEL = 0,
+	PARSER_COUNTRY,
+	PARSER_PROVIDER,
+	PARSER_METHOD_GSM,
+	PARSER_METHOD_GSM_APN,
+	PARSER_METHOD_CDMA,
+	PARSER_DONE,
+	PARSER_ERROR
+} ParseContextState;
+
+typedef struct {
+	char *mccmnc;
+	NMServiceProvidersGsmApnCallback callback;
+	gpointer user_data;
+	GCancellable *cancellable;
+	GMarkupParseContext *ctx;
+	char buffer[4096];
+
+	char *text_buffer;
+	ParseContextState state;
+
+	gboolean mccmnc_matched;
+	gboolean found_internet_apn;
+	char *apn;
+	char *username;
+	char *password;
+	char *gateway;
+	char *auth_method;
+	GSList *dns;
+} ParseContext;
+
+/*****************************************************************************/
+
+static void
+parser_toplevel_start (ParseContext *parse_context,
+                       const char *name,
+                       const char **attribute_names,
+                       const char **attribute_values)
+{
+	int i;
+
+	if (strcmp (name, "serviceproviders") == 0) {
+		for (i = 0; attribute_names && attribute_names[i]; i++) {
+			if (strcmp (attribute_names[i], "format") == 0) {
+				if (strcmp (attribute_values[i], "2.0")) {
+					g_warning ("%s: mobile broadband provider database format '%s'"
+					           " not supported.", __func__, attribute_values[i]);
+					parse_context->state = PARSER_ERROR;
+					break;
+				}
+			}
+		}
+	} else if (strcmp (name, "country") == 0) {
+		parse_context->state = PARSER_COUNTRY;
+	}
+}
+
+static void
+parser_country_start (ParseContext *parse_context,
+                      const char *name,
+                      const char **attribute_names,
+                      const char **attribute_values)
+{
+	if (strcmp (name, "provider") == 0)
+		parse_context->state = PARSER_PROVIDER;
+}
+
+static void
+parser_provider_start (ParseContext *parse_context,
+                       const char *name,
+                       const char **attribute_names,
+                       const char **attribute_values)
+{
+	parse_context->mccmnc_matched = FALSE;
+	if (strcmp (name, "gsm") == 0)
+		parse_context->state = PARSER_METHOD_GSM;
+	else if (strcmp (name, "cdma") == 0)
+		parse_context->state = PARSER_METHOD_CDMA;
+}
+
+static void
+parser_gsm_start (ParseContext *parse_context,
+                  const char *name,
+                  const char **attribute_names,
+                  const char **attribute_values)
+{
+	int i;
+
+	if (strcmp (name, "network-id") == 0) {
+		const char *mcc = NULL, *mnc = NULL;
+
+		for (i = 0; attribute_names && attribute_names[i]; i++) {
+			if (strcmp (attribute_names[i], "mcc") == 0)
+				mcc = attribute_values[i];
+			else if (strcmp (attribute_names[i], "mnc") == 0)
+				mnc = attribute_values[i];
+			if (mcc && strlen (mcc) && mnc && strlen (mnc)) {
+				char *mccmnc = g_strdup_printf ("%s%s", mcc, mnc);
+
+				if (strcmp (mccmnc, parse_context->mccmnc) == 0)
+					parse_context->mccmnc_matched = TRUE;
+				g_free (mccmnc);
+				break;
+			}
+		}
+	} else if (strcmp (name, "apn") == 0) {
+		parse_context->found_internet_apn = FALSE;
+		g_clear_pointer (&parse_context->apn, g_free);
+		g_clear_pointer (&parse_context->username, g_free);
+		g_clear_pointer (&parse_context->password, g_free);
+		g_clear_pointer (&parse_context->gateway, g_free);
+		g_clear_pointer (&parse_context->auth_method, g_free);
+		g_slist_free_full (parse_context->dns, g_free);
+		parse_context->dns = NULL;
+
+		for (i = 0; attribute_names && attribute_names[i]; i++) {
+			if (strcmp (attribute_names[i], "value") == 0) {
+				parse_context->state = PARSER_METHOD_GSM_APN;
+				parse_context->apn = g_strstrip (g_strdup (attribute_values[i]));
+				break;
+			}
+		}
+	}
+}
+
+static void
+parser_gsm_apn_start (ParseContext *parse_context,
+                  const char *name,
+                  const char **attribute_names,
+                  const char **attribute_values)
+{
+	int i;
+
+	if (strcmp (name, "usage") == 0) {
+		for (i = 0; attribute_names && attribute_names[i]; i++) {
+			if (   (strcmp (attribute_names[i], "type") == 0)
+			    && (strcmp (attribute_values[i], "internet") == 0)) {
+				parse_context->found_internet_apn = TRUE;
+				break;
+			}
+		}
+	} else if (strcmp (name, "authentication") == 0) {
+		for (i = 0; attribute_names && attribute_names[i]; i++) {
+			if (strcmp (attribute_names[i], "method") == 0) {
+				g_clear_pointer (&parse_context->auth_method, g_free);
+				parse_context->auth_method = g_strstrip (g_strdup (attribute_values[i]));
+				break;
+			}
+		}
+	}
+}
+
+static void
+parser_start_element (GMarkupParseContext *context,
+                             const char *element_name,
+                             const char **attribute_names,
+                             const char **attribute_values,
+                             gpointer user_data,
+                             GError **error)
+{
+	ParseContext *parse_context = user_data;
+
+	g_clear_pointer (&parse_context->text_buffer, g_free);
+
+	switch (parse_context->state) {
+	case PARSER_TOPLEVEL:
+		parser_toplevel_start (parse_context, element_name, attribute_names, attribute_values);
+		break;
+	case PARSER_COUNTRY:
+		parser_country_start (parse_context, element_name, attribute_names, attribute_values);
+		break;
+	case PARSER_PROVIDER:
+		parser_provider_start (parse_context, element_name, attribute_names, attribute_values);
+		break;
+	case PARSER_METHOD_GSM:
+		parser_gsm_start (parse_context, element_name, attribute_names, attribute_values);
+		break;
+	case PARSER_METHOD_GSM_APN:
+		parser_gsm_apn_start (parse_context, element_name, attribute_names, attribute_values);
+		break;
+	case PARSER_METHOD_CDMA:
+		break;
+	case PARSER_ERROR:
+		break;
+	case PARSER_DONE:
+		break;
+	}
+}
+
+static void
+parser_country_end (ParseContext *parse_context,
+                    const char *name)
+{
+	if (strcmp (name, "country") == 0) {
+		g_clear_pointer (&parse_context->text_buffer, g_free);
+		parse_context->state = PARSER_TOPLEVEL;
+	}
+}
+
+static void
+parser_provider_end (ParseContext *parse_context,
+                     const char *name)
+{
+	if (strcmp (name, "provider") == 0) {
+		g_clear_pointer (&parse_context->text_buffer, g_free);
+		parse_context->state = PARSER_COUNTRY;
+	}
+}
+
+static void
+parser_gsm_end (ParseContext *parse_context,
+                const char *name)
+{
+	if (strcmp (name, "gsm") == 0) {
+		g_clear_pointer (&parse_context->text_buffer, g_free);
+		parse_context->state = PARSER_PROVIDER;
+	}
+}
+
+static void
+parser_gsm_apn_end (ParseContext *parse_context,
+                    const char *name)
+{
+	if (strcmp (name, "username") == 0) {
+		g_clear_pointer (&parse_context->username, g_free);
+		parse_context->username = g_steal_pointer (&parse_context->text_buffer);
+	} else if (strcmp (name, "password") == 0) {
+		g_clear_pointer (&parse_context->password, g_free);
+		parse_context->password = g_steal_pointer (&parse_context->text_buffer);
+	} else if (strcmp (name, "dns") == 0) {
+		parse_context->dns = g_slist_prepend (parse_context->dns,
+		                                   g_steal_pointer (&parse_context->text_buffer));
+	} else if (strcmp (name, "gateway") == 0) {
+		g_clear_pointer (&parse_context->gateway, g_free);
+		parse_context->gateway = g_steal_pointer (&parse_context->text_buffer);
+	} else if (strcmp (name, "apn") == 0) {
+		g_clear_pointer (&parse_context->text_buffer, g_free);
+
+		if (parse_context->mccmnc_matched && parse_context->found_internet_apn)
+			parse_context->state = PARSER_DONE;
+		else
+			parse_context->state = PARSER_METHOD_GSM;
+
+	}
+}
+
+static void
+parser_cdma_end (ParseContext *parse_context,
+                 const char *name)
+{
+	if (strcmp (name, "cdma") == 0) {
+		g_clear_pointer (&parse_context->text_buffer, g_free);
+		parse_context->state = PARSER_PROVIDER;
+	}
+}
+
+static void
+parser_end_element (GMarkupParseContext *context,
+                           const char *element_name,
+                           gpointer user_data,
+                           GError **error)
+{
+	ParseContext *parse_context = user_data;
+
+	switch (parse_context->state) {
+	case PARSER_TOPLEVEL:
+		break;
+	case PARSER_COUNTRY:
+		parser_country_end (parse_context, element_name);
+		break;
+	case PARSER_PROVIDER:
+		parser_provider_end (parse_context, element_name);
+		break;
+	case PARSER_METHOD_GSM:
+		parser_gsm_end (parse_context, element_name);
+		break;
+	case PARSER_METHOD_GSM_APN:
+		parser_gsm_apn_end (parse_context, element_name);
+		break;
+	case PARSER_METHOD_CDMA:
+		parser_cdma_end (parse_context, element_name);
+		break;
+	case PARSER_ERROR:
+		break;
+	case PARSER_DONE:
+		break;
+	}
+}
+
+static void
+parser_text (GMarkupParseContext *context,
+             const char *text,
+             gsize text_len,
+             gpointer user_data,
+             GError **error)
+{
+	ParseContext *parse_context = user_data;
+
+	g_free (parse_context->text_buffer);
+	parse_context->text_buffer = g_strdup (text);
+}
+
+static const GMarkupParser parser = {
+	.start_element  = parser_start_element,
+	.end_element    = parser_end_element,
+	.text           = parser_text,
+	.passthrough    = NULL,
+	.error          = NULL,
+};
+
+/*****************************************************************************/
+
+static void
+finish_parse_context (ParseContext *parse_context, GError *error)
+{
+	if (parse_context->callback) {
+		if (error) {
+			parse_context->callback (NULL, NULL, NULL, NULL, NULL,
+			                         NULL, error,
+			                         parse_context->user_data);
+		} else {
+			parse_context->callback (parse_context->apn,
+			                         parse_context->username,
+			                         parse_context->password,
+			                         parse_context->gateway,
+			                         parse_context->auth_method,
+			                         parse_context->dns,
+			                         error,
+			                         parse_context->user_data);
+		}
+	}
+
+	g_free (parse_context->mccmnc);
+	g_markup_parse_context_free (parse_context->ctx);
+
+	g_free (parse_context->text_buffer);
+	g_free (parse_context->apn);
+	g_free (parse_context->username);
+	g_free (parse_context->password);
+	g_free (parse_context->gateway);
+	g_free (parse_context->auth_method);
+	g_slist_free_full (parse_context->dns, g_free);
+
+	g_slice_free (ParseContext, parse_context);
+}
+
+static void
+read_next_chunk (GInputStream *stream, ParseContext *parse_context);
+
+static void
+stream_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+	GInputStream *stream = G_INPUT_STREAM (source_object);
+	ParseContext *parse_context = user_data;
+	gssize len;
+	GError *error = NULL;
+
+	len = g_input_stream_read_finish (stream, res, &error);
+	if (len == -1) {
+		g_prefix_error (&error, "Error reading service provider database: ");
+		finish_parse_context (parse_context, error);
+		g_clear_error (&error);
+		return;
+	}
+
+	if (len == 0) {
+		g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "Operator ID '%s' not found in service provider database",
+		             parse_context->mccmnc);
+		finish_parse_context (parse_context, error);
+		g_clear_error (&error);
+		return;
+	}
+
+	if (!g_markup_parse_context_parse (parse_context->ctx, parse_context->buffer, len, &error)) {
+		g_prefix_error (&error, "Error parsing service provider database: ");
+		finish_parse_context (parse_context, error);
+		g_clear_error (&error);
+		return;
+	}
+
+	if (parse_context->state == PARSER_DONE) {
+		finish_parse_context (parse_context, NULL);
+		return;
+	}
+
+	read_next_chunk (stream, parse_context);
+}
+
+static void
+read_next_chunk (GInputStream *stream, ParseContext *parse_context)
+{
+	g_input_stream_read_async (stream,
+	                           parse_context->buffer,
+	                           sizeof (parse_context->buffer),
+	                           G_PRIORITY_DEFAULT,
+	                           parse_context->cancellable,
+	                           stream_read_cb,
+	                           parse_context);
+}
+
+static void
+file_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+	GFile *file = G_FILE (source_object);
+	ParseContext *parse_context = user_data;
+	GFileInputStream *stream;
+	gs_free_error GError *error = NULL;
+
+	stream = g_file_read_finish (file, res, &error);
+	if (!stream) {
+		g_prefix_error (&error, "Error opening service provider database: ");
+		finish_parse_context (parse_context, error);
+		return;
+	}
+
+	read_next_chunk (G_INPUT_STREAM (stream), parse_context);
+
+	g_object_unref (stream);
+}
+
+/*****************************************************************************/
+
+void
+nm_service_providers_find_gsm_apn (const char *service_providers,
+                                   const char *mccmnc,
+                                   GCancellable *cancellable,
+                                   NMServiceProvidersGsmApnCallback callback,
+                                   gpointer user_data)
+{
+	GFile *file;
+	ParseContext *parse_context;
+
+	parse_context = g_slice_new0 (ParseContext);
+	parse_context->mccmnc = g_strdup (mccmnc);
+	parse_context->cancellable = cancellable;
+	parse_context->callback = callback;
+	parse_context->user_data = user_data;
+	parse_context->ctx = g_markup_parse_context_new (&parser, 0, parse_context, NULL);
+
+	file = g_file_new_for_path (service_providers);
+
+	g_file_read_async (file, G_PRIORITY_DEFAULT, cancellable, file_read_cb, parse_context);
+
+	g_object_unref (file);
+}
diff --git a/src/devices/wwan/nm-service-providers.h b/src/devices/wwan/nm-service-providers.h
new file mode 100644
index 00000000..35ad2fc1
--- /dev/null
+++ b/src/devices/wwan/nm-service-providers.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_SERVICE_PROVIDERS_H__
+#define __NETWORKMANAGER_SERVICE_PROVIDERS_H__
+
+typedef void (*NMServiceProvidersGsmApnCallback) (const char *apn,
+                                                  const char *username,
+                                                  const char *password,
+                                                  const char *gateway,
+                                                  const char *auth_method,
+                                                  const GSList *dns,
+                                                  GError *error,
+                                                  gpointer user_data);
+
+void nm_service_providers_find_gsm_apn (const char *service_providers,
+                                        const char *mccmnc,
+                                        GCancellable *cancellable,
+                                        NMServiceProvidersGsmApnCallback callback,
+                                        gpointer user_data);
+
+#endif /* __NETWORKMANAGER_SERVICE_PROVIDERS_H__ */
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index a0e5c160..9a85f936 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -77,11 +63,10 @@ modem_added_cb (NMModemManager *manager,
                 gpointer user_data)
 {
 	NMWwanFactory *self = NM_WWAN_FACTORY (user_data);
-	NMDevice *device;
+	gs_unref_object NMDevice *device = NULL;
 	const char *driver;
 
-	/* Do nothing if the modem was consumed by some other plugin */
-	if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem)))
+	if (nm_modem_is_claimed (modem))
 		return;
 
 	driver = nm_modem_get_driver (modem);
@@ -90,17 +75,16 @@ modem_added_cb (NMModemManager *manager,
 	 * it.  The rfcomm port (and thus the modem) gets created automatically
 	 * by the Bluetooth code during the connection process.
 	 */
-	if (driver && strstr (driver, "bluetooth")) {
-		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)",
-		             nm_modem_get_control_port (modem));
+	if (   driver
+	    && strstr (driver, "bluetooth")) {
+		nm_log_dbg (LOGD_MB, "WWAN factory ignores bluetooth modem '%s' which should be handled by bluetooth plugin",
+		            nm_modem_get_control_port (modem));
 		return;
 	}
 
 	/* Make the new modem device */
 	device = nm_device_modem_new (modem);
-	g_assert (device);
 	g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
-	g_object_unref (device);
 }
 
 static NMDevice *
diff --git a/src/devices/wwan/tests/test-service-providers.c b/src/devices/wwan/tests/test-service-providers.c
new file mode 100644
index 00000000..33402cd5
--- /dev/null
+++ b/src/devices/wwan/tests/test-service-providers.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat
+ */
+
+#include "nm-default.h"
+
+#include "nm-service-providers.h"
+
+#include "nm-test-utils-core.h"
+
+static void
+test_positive_cb (const char *apn,
+                  const char *username,
+                  const char *password,
+                  const char *gateway,
+                  const char *auth_method,
+                  const GSList *dns,
+                  GError *error,
+                  gpointer user_data)
+{
+	GMainLoop *loop = user_data;
+
+	g_main_loop_quit (loop);
+	g_assert_no_error (error);
+	g_assert_cmpstr (apn, ==, "gprs.example.com");
+	g_assert_cmpstr (username, ==, "praise");
+	g_assert_cmpstr (password, ==, "santa");
+	g_assert_cmpstr (gateway, ==, "192.0.2.3");
+	g_assert_cmpstr (auth_method, ==, "pap");
+
+	g_assert_nonnull (dns);
+	g_assert_cmpstr (dns->data, ==, "192.0.2.2");
+	dns = dns->next;
+	g_assert_nonnull (dns);
+	g_assert_cmpstr (dns->data, ==, "192.0.2.1");
+	g_assert_null (dns->next);
+}
+
+static void
+test_positive (void)
+{
+	GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+
+	nm_service_providers_find_gsm_apn (NM_BUILD_SRCDIR"/src/devices/wwan/tests/test-service-providers.xml",
+	                                   "13337", NULL, test_positive_cb, loop);
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
+}
+
+/*****************************************************************************/
+
+static void
+test_negative_cb (const char *apn,
+                  const char *username,
+                  const char *password,
+                  const char *gateway,
+                  const char *auth_method,
+                  const GSList *dns,
+                  GError *error,
+                  gpointer user_data)
+{
+	GMainLoop *loop = user_data;
+
+	g_main_loop_quit (loop);
+	g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN);
+}
+
+static void
+test_negative (void)
+{
+	GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+
+	nm_service_providers_find_gsm_apn (NM_BUILD_SRCDIR"/src/devices/wwan/tests/test-service-providers.xml",
+	                                   "78130", NULL, test_negative_cb, loop);
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
+}
+
+/*****************************************************************************/
+
+static void
+test_nonexistent_cb (const char *apn,
+                     const char *username,
+                     const char *password,
+                     const char *gateway,
+                     const char *auth_method,
+                     const GSList *dns,
+                     GError *error,
+                     gpointer user_data)
+{
+	GMainLoop *loop = user_data;
+
+	g_main_loop_quit (loop);
+	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_AGAIN);
+}
+
+static void
+test_nonexistent (void)
+{
+	GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+
+	nm_service_providers_find_gsm_apn ("nonexistent.xml", "13337", NULL,
+	                                   test_nonexistent_cb, loop);
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
+}
+
+/*****************************************************************************/
+
+NMTST_DEFINE ();
+
+int
+main (int argc, char **argv)
+{
+	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
+
+	g_test_add_func ("/service-providers/positive", test_positive);
+	g_test_add_func ("/service-providers/negative", test_negative);
+	g_test_add_func ("/service-providers/nonexistent", test_nonexistent);
+
+	return g_test_run ();
+}
+
diff --git a/src/devices/wwan/tests/test-service-providers.xml b/src/devices/wwan/tests/test-service-providers.xml
new file mode 100644
index 00000000..f0ca2deb
--- /dev/null
+++ b/src/devices/wwan/tests/test-service-providers.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding='utf-8'?>
+<!DOCTYPE serviceproviders SYSTEM "serviceproviders.2.dtd">
+
+<serviceproviders format="2.0">
+
+<country code="feh">
+	<provider>
+		<name>Sophia</name>
+		<gsm>
+			<network-id mcc="666" mnc="999"/>
+			<apn value="access.example.com">
+				<plan type="postpaid"/>
+				<usage type="internet"/>
+				<name>APN</name>
+				<dns>192.0.2.1</dns>
+				<dns>192.0.2.2</dns>
+			</apn>
+		</gsm>
+	</provider>
+</country>
+
+<country code="meh">
+	<provider>
+		<name>Demiurge</name>
+		<gsm>
+			<network-id mcc="133" mnc="37"/>
+			<network-id mcc="133" mnc="666"/>
+			<apn value="mms">
+				<usage type="mms"/>
+				<name>Unsolicited Nudes MMS</name>
+				<username>mms</username>
+				<password>mms</password>
+				<mmsc>http://mms.example.com/</mmsc>
+				<mmsproxy>192.0.2.1:8080</mmsproxy>
+			</apn>
+			<apn value="gprs.example.com">
+				<plan type="postpaid"/>
+				<usage type="internet"/>
+				<name>GPRS</name>
+				<username>praise</username>
+				<password>santa</password>
+				<dns>192.0.2.1</dns>
+				<dns>192.0.2.2</dns>
+				<gateway>192.0.2.3</gateway>
+				<authentication method="pap"/>
+			</apn>
+			<apn value="second.example.com">
+				<plan type="postpaid"/>
+				<usage type="internet"/>
+				<name>Second</name>
+				<username>worship</username>
+				<password>doom</password>
+			</apn>
+		</gsm>
+	</provider>
+
+	<provider>
+		<name>Personal</name>
+		<gsm>
+			<network-id mcc="666" mnc="999"/>
+			<apn value="access.example.com">
+				<plan type="postpaid"/>
+				<usage type="internet"/>
+				<name>APN</name>
+				<dns>192.0.2.1</dns>
+				<dns>192.0.2.2</dns>
+			</apn>
+		</gsm>
+	</provider>
+</country>
+
+</serviceproviders>
+
diff --git a/src/dhcp/meson.build b/src/dhcp/meson.build
index c1f28be0..609fe663 100644
--- a/src/dhcp/meson.build
+++ b/src/dhcp/meson.build
@@ -1,6 +1,6 @@
 name = 'nm-dhcp-helper'
 
-cflags = [
+c_flags = [
   '-DG_LOG_DOMAIN="@0@"'.format(name),
   '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB',
 ]
@@ -8,8 +8,8 @@ cflags = [
 executable(
   name,
   name + '.c',
-  dependencies: libnm_core_dep,
-  c_args: cflags,
+  dependencies: glib_nm_default_dep,
+  c_args: c_flags,
   link_args: ldflags_linker_script_binary,
   link_depends: linker_script_binary,
   install: true,
diff --git a/src/dhcp/nm-dhcp-client-logging.h b/src/dhcp/nm-dhcp-client-logging.h
index d9b53f58..4e69c698 100644
--- a/src/dhcp/nm-dhcp-client-logging.h
+++ b/src/dhcp/nm-dhcp-client-logging.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c
index 9f585c5d..0a07b26c 100644
--- a/src/dhcp/nm-dhcp-client.c
+++ b/src/dhcp/nm-dhcp-client.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -59,7 +46,10 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient,
 	PROP_ROUTE_TABLE,
 	PROP_TIMEOUT,
 	PROP_UUID,
+	PROP_IAID,
+	PROP_IAID_EXPLICIT,
 	PROP_HOSTNAME,
+	PROP_HOSTNAME_FLAGS,
 );
 
 typedef struct _NMDhcpClientPrivate {
@@ -78,12 +68,15 @@ typedef struct _NMDhcpClientPrivate {
 	guint32      route_table;
 	guint32      route_metric;
 	guint32      timeout;
+	guint32      iaid;
 	NMDhcpState  state;
+	NMDhcpHostnameFlags hostname_flags;
 	bool         info_only:1;
 	bool         use_fqdn:1;
+	bool         iaid_explicit:1;
 } NMDhcpClientPrivate;
 
-G_DEFINE_TYPE_EXTENDED (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
+G_DEFINE_ABSTRACT_TYPE (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT)
 
 #define NM_DHCP_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMDhcpClient, NM_IS_DHCP_CLIENT)
 
@@ -199,6 +192,22 @@ nm_dhcp_client_get_timeout (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->timeout;
 }
 
+guint32
+nm_dhcp_client_get_iaid (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), 0);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iaid;
+}
+
+gboolean
+nm_dhcp_client_get_iaid_explicit (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iaid_explicit;
+}
+
 GBytes *
 nm_dhcp_client_get_client_id (NMDhcpClient *self)
 {
@@ -279,6 +288,14 @@ nm_dhcp_client_get_hostname (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
 }
 
+NMDhcpHostnameFlags
+nm_dhcp_client_get_hostname_flags (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NM_DHCP_HOSTNAME_FLAG_NONE);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname_flags;
+}
+
 gboolean
 nm_dhcp_client_get_info_only (NMDhcpClient *self)
 {
@@ -419,6 +436,17 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 	if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND))
 		return;
 
+	if (_LOGI_ENABLED ()) {
+		gs_free const char **keys = NULL;
+		guint i, nkeys;
+
+		keys = nm_utils_strdict_get_keys (options, TRUE, &nkeys);
+		for (i = 0; i < nkeys; i++) {
+			_LOGI ("option %-20s => '%s'", keys[i],
+		               (char *) g_hash_table_lookup (options, keys[i]));
+		}
+	}
+
 	if (   priv->addr_family == AF_INET6
 	    && new_state == NM_DHCP_STATE_BOUND) {
 		char *start, *iaid;
@@ -686,7 +714,7 @@ nm_dhcp_client_stop (NMDhcpClient *self, gboolean release)
 		_LOGI ("canceled DHCP transaction, DHCP client pid %d", old_pid);
 	else
 		_LOGI ("canceled DHCP transaction");
-	g_assert (priv->pid == -1);
+	nm_assert (priv->pid == -1);
 
 	nm_dhcp_client_set_state (self, NM_DHCP_STATE_DONE, NULL, NULL);
 }
@@ -807,6 +835,15 @@ maybe_add_option (NMDhcpClient *self,
 	}
 }
 
+void
+nm_dhcp_client_emit_ipv6_prefix_delegated (NMDhcpClient *self,
+                                           const NMPlatformIP6Address *prefix)
+{
+	g_signal_emit (G_OBJECT (self),
+	               signals[SIGNAL_PREFIX_DELEGATED], 0,
+	               prefix);
+}
+
 gboolean
 nm_dhcp_client_handle_event (gpointer unused,
                              const char *iface,
@@ -853,15 +890,6 @@ nm_dhcp_client_handle_event (gpointer unused,
 			g_variant_unref (value);
 		}
 
-		if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
-			GHashTableIter hash_iter;
-			gpointer key, val;
-
-			g_hash_table_iter_init (&hash_iter, str_options);
-			while (g_hash_table_iter_next (&hash_iter, &key, &val))
-				_LOGD ("option '%s'=>'%s'", (const char *) key, (const char *) val);
-		}
-
 		/* Create the IP config */
 		if (g_hash_table_size (str_options) > 0) {
 			if (priv->addr_family == AF_INET) {
@@ -887,9 +915,7 @@ nm_dhcp_client_handle_event (gpointer unused,
 		/* If we got an IPv6 prefix to delegate, we don't change the state
 		 * of the DHCP client instance. Instead, we just signal the prefix
 		 * to the device. */
-		g_signal_emit (G_OBJECT (self),
-		               signals[SIGNAL_PREFIX_DELEGATED], 0,
-		               &prefix);
+		nm_dhcp_client_emit_ipv6_prefix_delegated (self, &prefix);
 	} else {
 		/* Fail if no valid IP config was received */
 		if (   new_state == NM_DHCP_STATE_BOUND
@@ -932,6 +958,12 @@ get_property (GObject *object, guint prop_id,
 	case PROP_UUID:
 		g_value_set_string (value, priv->uuid);
 		break;
+	case PROP_IAID:
+		g_value_set_uint (value, priv->iaid);
+		break;
+	case PROP_IAID_EXPLICIT:
+		g_value_set_boolean (value, priv->iaid_explicit);
+		break;
 	case PROP_HOSTNAME:
 		g_value_set_string (value, priv->hostname);
 		break;
@@ -1001,10 +1033,22 @@ set_property (GObject *object, guint prop_id,
 		/* construct-only */
 		priv->uuid = g_value_dup_string (value);
 		break;
+	case PROP_IAID:
+		/* construct-only */
+		priv->iaid = g_value_get_uint (value);
+		break;
+	case PROP_IAID_EXPLICIT:
+		/* construct-only */
+		priv->iaid_explicit = g_value_get_boolean (value);
+		break;
 	case PROP_HOSTNAME:
 		/* construct-only */
 		priv->hostname = g_value_dup_string (value);
 		break;
+	case PROP_HOSTNAME_FLAGS:
+		/* construct-only */
+		priv->hostname_flags = g_value_get_uint (value);
+		break;
 	case PROP_ROUTE_TABLE:
 		priv->route_table = g_value_get_uint (value);
 		break;
@@ -1120,12 +1164,30 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_IAID] =
+	    g_param_spec_uint (NM_DHCP_CLIENT_IAID, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_IAID_EXPLICIT] =
+	    g_param_spec_boolean (NM_DHCP_CLIENT_IAID_EXPLICIT, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_HOSTNAME] =
 	    g_param_spec_string (NM_DHCP_CLIENT_HOSTNAME, "", "",
 	                         NULL,
 	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_HOSTNAME_FLAGS] =
+	    g_param_spec_uint (NM_DHCP_CLIENT_HOSTNAME_FLAGS, "", "",
+	                       0, G_MAXUINT32, NM_DHCP_HOSTNAME_FLAG_NONE,
+	                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_ROUTE_TABLE] =
 	    g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_TABLE, "", "",
 	                       0, G_MAXUINT32, RT_TABLE_MAIN,
diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h
index 9eb76f33..6a431fa8 100644
--- a/src/dhcp/nm-dhcp-client.h
+++ b/src/dhcp/nm-dhcp-client.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  */
 
@@ -46,6 +34,9 @@
 #define NM_DHCP_CLIENT_ROUTE_TABLE      "route-table"
 #define NM_DHCP_CLIENT_TIMEOUT          "timeout"
 #define NM_DHCP_CLIENT_UUID             "uuid"
+#define NM_DHCP_CLIENT_IAID             "iaid"
+#define NM_DHCP_CLIENT_IAID_EXPLICIT    "iaid-explicit"
+#define NM_DHCP_CLIENT_HOSTNAME_FLAGS   "hostname-flags"
 
 #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed"
 #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated"
@@ -142,10 +133,16 @@ void nm_dhcp_client_set_route_metric (NMDhcpClient *self, guint32 route_metric);
 
 guint32 nm_dhcp_client_get_timeout (NMDhcpClient *self);
 
+guint32 nm_dhcp_client_get_iaid (NMDhcpClient *self);
+
+gboolean nm_dhcp_client_get_iaid_explicit (NMDhcpClient *self);
+
 GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self);
 
 const char *nm_dhcp_client_get_hostname (NMDhcpClient *self);
 
+NMDhcpHostnameFlags nm_dhcp_client_get_hostname_flags (NMDhcpClient *self);
+
 gboolean nm_dhcp_client_get_info_only (NMDhcpClient *self);
 
 gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self);
@@ -202,20 +199,28 @@ void nm_dhcp_client_set_client_id_bin (NMDhcpClient *self,
                                        const guint8 *client_id,
                                        gsize len);
 
+void nm_dhcp_client_emit_ipv6_prefix_delegated (NMDhcpClient *self,
+                                                const NMPlatformIP6Address *prefix);
+
 /*****************************************************************************
  * Client data
  *****************************************************************************/
 
 typedef struct {
-	GType (*get_type)(void);
+	GType (*get_type) (void);
+	GType (*get_type_per_addr_family) (int addr_family);
 	const char *name;
 	const char *(*get_path) (void);
+	bool experimental:1;
 } NMDhcpClientFactory;
 
+GType nm_dhcp_nettools_get_type (void);
+
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_internal;
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_systemd;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_nettools;
 
 #endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
index 98f8c13a..f31c493c 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp/nm-dhcp-dhclient-utils.c
@@ -1,18 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2011 Red Hat, Inc.
  */
 
@@ -106,21 +93,11 @@ grab_request_options (GPtrArray *store, const char* line)
 }
 
 static void
-add_hostname4 (GString *str, const char *hostname, gboolean use_fqdn)
-{
-	if (hostname) {
-		if (use_fqdn) {
-			g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
-			g_string_append (str,
-			                 "send fqdn.encoded on;\n"
-			                 "send fqdn.server-update on;\n");
-		} else
-			g_string_append_printf (str, HOSTNAME4_FORMAT "\n", hostname);
-	}
-}
-
-static void
-add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean use_fqdn)
+add_ip4_config (GString *str,
+                GBytes *client_id,
+                const char *hostname,
+                gboolean use_fqdn,
+                NMDhcpHostnameFlags hostname_flags)
 {
 	if (client_id) {
 		const char *p;
@@ -128,7 +105,7 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 		guint i;
 
 		p = g_bytes_get_data (client_id, &l);
-		g_assert (p);
+		nm_assert (p);
 
 		/* Allow type 0 (non-hardware address) to be represented as a string
 		 * as long as all the characters are printable.
@@ -156,7 +133,27 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 		g_string_append (str, "; # added by NetworkManager\n");
 	}
 
-	add_hostname4 (str, hostname, use_fqdn);
+	if (hostname) {
+		if (use_fqdn) {
+			g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
+
+			g_string_append_printf (str, FQDN_TAG_PREFIX "encoded %s;\n",
+			                          (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED)
+			                        ? "on"
+			                        : "off");
+
+			g_string_append_printf (str, FQDN_TAG_PREFIX "server-update %s;\n",
+			                          (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)
+			                        ? "on"
+			                        : "off");
+
+			g_string_append_printf (str, FQDN_TAG_PREFIX "no-client-update %s;\n",
+			                          (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)
+			                        ? "on"
+			                        : "off");
+		} else
+			g_string_append_printf (str, HOSTNAME4_FORMAT "\n", hostname);
+	}
 
 	g_string_append_c (str, '\n');
 
@@ -172,12 +169,16 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 }
 
 static void
-add_hostname6 (GString *str, const char *hostname)
+add_hostname6 (GString *str,
+               const char *hostname,
+               NMDhcpHostnameFlags hostname_flags)
 {
 	if (hostname) {
 		g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
-		g_string_append (str,
-		                 "send fqdn.server-update on;\n");
+		if (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)
+			g_string_append (str, FQDN_TAG_PREFIX "server-update on;\n");
+		if (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)
+			g_string_append (str, FQDN_TAG_PREFIX "no-client-update on;\n");
 		g_string_append_c (str, '\n');
 	}
 }
@@ -284,6 +285,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
                                 const char *hostname,
                                 guint32 timeout,
                                 gboolean use_fqdn,
+                                NMDhcpHostnameFlags hostname_flags,
                                 const char *orig_path,
                                 const char *orig_contents,
                                 GBytes **out_new_client_id)
@@ -450,7 +452,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 	}
 
 	if (addr_family == AF_INET) {
-		add_ip4_config (new_contents, client_id, hostname, use_fqdn);
+		add_ip4_config (new_contents, client_id, hostname, use_fqdn, hostname_flags);
 		add_request (reqs, "rfc3442-classless-static-routes");
 		add_request (reqs, "ms-classless-static-routes");
 		add_request (reqs, "static-routes");
@@ -458,7 +460,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 		add_request (reqs, "ntp-servers");
 		add_request (reqs, "root-path");
 	} else {
-		add_hostname6 (new_contents, hostname);
+		add_hostname6 (new_contents, hostname, hostname_flags);
 		add_request (reqs, "dhcp6.name-servers");
 		add_request (reqs, "dhcp6.domain-search");
 
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h
index 8ca893c3..0cf53887 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp/nm-dhcp-dhclient-utils.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 Red Hat, Inc.
  */
 
@@ -28,6 +16,7 @@ char *nm_dhcp_dhclient_create_config (const char *interface,
                                       const char *hostname,
                                       guint32 timeout,
                                       gboolean use_fqdn,
+                                      NMDhcpHostnameFlags hostname_flags,
                                       const char *orig_path,
                                       const char *orig_contents,
                                       GBytes **out_new_client_id);
diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c
index 54b50479..869966fb 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -1,19 +1,5 @@
-/* nm-dhcp-dhclient.c - dhclient specific hooks for 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, 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2012 Red Hat, Inc.
  */
 
@@ -38,7 +24,6 @@
 #include "nm-glib-aux/nm-dedup-multi.h"
 
 #include "nm-utils.h"
-#include "nm-config.h"
 #include "nm-dhcp-dhclient-utils.h"
 #include "nm-dhcp-manager.h"
 #include "NetworkManagerUtils.h"
@@ -118,35 +103,14 @@ get_dhclient_leasefile (int addr_family,
                         const char *uuid,
                         char **out_preferred_path)
 {
-	gs_free char *rundir_path = NULL;
 	gs_free char *path = NULL;
 
-	/* First, see if the lease file is in /run */
-	rundir_path = g_strdup_printf (NMRUNDIR "/dhclient%s-%s-%s.lease",
-	                               _addr_family_to_path_part (addr_family),
-	                               uuid,
-	                               iface);
-
-	if (g_file_test (rundir_path, G_FILE_TEST_EXISTS)) {
-		NM_SET_OUT (out_preferred_path, g_strdup (rundir_path));
-		return g_steal_pointer (&rundir_path);
-	}
-
-	/* /var/lib/NetworkManager is the preferred leasefile path */
-	path = g_strdup_printf (NMSTATEDIR "/dhclient%s-%s-%s.lease",
-	                        _addr_family_to_path_part (addr_family),
-	                        uuid,
-	                        iface);
-
-	if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+	if (nm_dhcp_utils_get_leasefile_path (addr_family, "dhclient", iface, uuid, &path)) {
 		NM_SET_OUT (out_preferred_path, g_strdup (path));
 		return g_steal_pointer (&path);
 	}
 
-	if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
-		NM_SET_OUT (out_preferred_path, g_steal_pointer (&rundir_path));
-	else
-		NM_SET_OUT (out_preferred_path, g_steal_pointer (&path));
+	NM_SET_OUT (out_preferred_path, g_steal_pointer (&path));
 
 	/* If the leasefile we're looking for doesn't exist yet in the new location
 	 * (eg, /var/lib/NetworkManager) then look in old locations to maintain
@@ -182,6 +146,7 @@ merge_dhclient_config (NMDhcpDhclient *self,
                        const char *hostname,
                        guint32 timeout,
                        gboolean use_fqdn,
+                       NMDhcpHostnameFlags hostname_flags,
                        const char *orig_path,
                        GBytes **out_new_client_id,
                        GError **error)
@@ -210,10 +175,11 @@ merge_dhclient_config (NMDhcpDhclient *self,
 	                                      hostname,
 	                                      timeout,
 	                                      use_fqdn,
+	                                      hostname_flags,
 	                                      orig_path,
 	                                      orig,
 	                                      out_new_client_id);
-	g_assert (new);
+	nm_assert (new);
 
 	return g_file_set_contents (conf_file,
 	                            new,
@@ -301,6 +267,7 @@ create_dhclient_config (NMDhcpDhclient *self,
                         const char *hostname,
                         guint32 timeout,
                         gboolean use_fqdn,
+                        NMDhcpHostnameFlags hostname_flags,
                         GBytes **out_new_client_id)
 {
 	gs_free char *orig = NULL;
@@ -328,6 +295,7 @@ create_dhclient_config (NMDhcpDhclient *self,
 	                            hostname,
 	                            timeout,
 	                            use_fqdn,
+	                            hostname_flags,
 	                            orig,
 	                            out_new_client_id,
 	                            &error)) {
@@ -527,6 +495,7 @@ ip4_start (NMDhcpClient *client,
 	                                          nm_dhcp_client_get_hostname (client),
 	                                          nm_dhcp_client_get_timeout (client),
 	                                          nm_dhcp_client_get_use_fqdn (client),
+	                                          nm_dhcp_client_get_hostname_flags (client),
 	                                          &new_client_id);
 	if (!priv->conf_file) {
 		nm_utils_error_set_literal (error,
@@ -558,6 +527,9 @@ ip6_start (NMDhcpClient *client,
 	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
 	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 
+	if (nm_dhcp_client_get_iaid_explicit (client))
+		_LOGW ("dhclient does not support specifying an IAID for DHCPv6, it will be ignored");
+
 	priv->conf_file = create_dhclient_config (self,
 	                                          AF_INET6,
 	                                          nm_dhcp_client_get_iface (client),
@@ -567,6 +539,7 @@ ip6_start (NMDhcpClient *client,
 	                                          nm_dhcp_client_get_hostname (client),
 	                                          nm_dhcp_client_get_timeout (client),
 	                                          TRUE,
+	                                          nm_dhcp_client_get_hostname_flags (client),
 	                                          NULL);
 	if (!priv->conf_file) {
 		nm_utils_error_set_literal (error,
@@ -723,7 +696,7 @@ nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = {
-	.name = "dhclient",
+	.name     = "dhclient",
 	.get_type = nm_dhcp_dhclient_get_type,
 	.get_path = nm_dhcp_dhclient_get_path,
 };
diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c
index 2d2113cc..f9cc0f0a 100644
--- a/src/dhcp/nm-dhcp-dhcpcanon.c
+++ b/src/dhcp/nm-dhcp-dhcpcanon.c
@@ -1,19 +1,5 @@
-/* nm-dhcp-dhcpcanon.c - dhcpcanon specific hooks for 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, 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 juga <juga at riseup dot net>
  */
 
@@ -248,7 +234,7 @@ nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = {
-	.name = "dhcpcanon",
+	.name     = "dhcpcanon",
 	.get_type = nm_dhcp_dhcpcanon_get_type,
 	.get_path = nm_dhcp_dhcpcanon_get_path,
 };
diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c
index c300bbe2..1690bce5 100644
--- a/src/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/dhcp/nm-dhcp-dhcpcd.c
@@ -1,22 +1,7 @@
-/* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Roy Marples
  * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, 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.
- *
  */
 
 #include "nm-default.h"
@@ -242,7 +227,7 @@ nm_dhcp_dhcpcd_class_init (NMDhcpDhcpcdClass *dhcpcd_class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd = {
-	.name = "dhcpcd",
+	.name     = "dhcpcd",
 	.get_type = nm_dhcp_dhcpcd_get_type,
 	.get_path = nm_dhcp_dhcpcd_get_path,
 };
diff --git a/src/dhcp/nm-dhcp-helper-api.h b/src/dhcp/nm-dhcp-helper-api.h
index c1a3c71c..a03049aa 100644
--- a/src/dhcp/nm-dhcp-helper-api.h
+++ b/src/dhcp/nm-dhcp-helper-api.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2016 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_DHCP_HELPER_API_H__
diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c
index 9acc4045..17f9db7f 100644
--- a/src/dhcp/nm-dhcp-helper.c
+++ b/src/dhcp/nm-dhcp-helper.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c
index 88aafeb0..a54b9643 100644
--- a/src/dhcp/nm-dhcp-listener.c
+++ b/src/dhcp/nm-dhcp-listener.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2014 - 2016 Red Hat, Inc.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 - 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -38,7 +25,7 @@
 
 /*****************************************************************************/
 
-const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = {
+const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6] = {
 	/* the order here matters, as we will try the plugins in this order to find
 	 * the first available plugin. */
 
@@ -52,6 +39,7 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = {
 	&_nm_dhcp_client_factory_dhcpcd,
 #endif
 	&_nm_dhcp_client_factory_internal,
+	&_nm_dhcp_client_factory_systemd,
 	&_nm_dhcp_client_factory_nettools,
 };
 
diff --git a/src/dhcp/nm-dhcp-listener.h b/src/dhcp/nm-dhcp-listener.h
index d9724062..c2b90dc3 100644
--- a/src/dhcp/nm-dhcp-listener.h
+++ b/src/dhcp/nm-dhcp-listener.h
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2014 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DHCP_LISTENER_H__
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index 304a7b99..10ed9589 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -1,22 +1,7 @@
-/* nm-dhcp-manager.c - Handle the DHCP daemon for 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, 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2013 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -32,6 +17,7 @@
 #include <stdio.h>
 
 #include "nm-glib-aux/nm-dedup-multi.h"
+#include "systemd/nm-sd-utils-shared.h"
 
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
@@ -89,10 +75,11 @@ _client_factory_available (const NMDhcpClientFactory *client_factory)
 	return NULL;
 }
 
-static const NMDhcpClientFactory *
-_client_factory_get_effective (const NMDhcpClientFactory *client_factory,
-                               int addr_family)
+static GType
+_client_factory_get_gtype (const NMDhcpClientFactory *client_factory,
+                           int addr_family)
 {
+	GType gtype;
 	nm_auto_unref_gtypeclass NMDhcpClientClass *klass = NULL;
 
 	nm_assert (client_factory);
@@ -118,23 +105,38 @@ _client_factory_get_effective (const NMDhcpClientFactory *client_factory,
 	 * to those plugins. But we don't intend to do so. The internal plugin is the way forward and
 	 * not extending other plugins. */
 
+	if (client_factory->get_type_per_addr_family)
+		gtype = client_factory->get_type_per_addr_family (addr_family);
+	else
+		gtype = client_factory->get_type ();
+
 	if (client_factory == &_nm_dhcp_client_factory_internal) {
-		/* already using internal plugin. Nothing to do. */
-		return client_factory;
+		/* we are already using the internal plugin. Nothing to do. */
+		goto out;
 	}
 
-	klass = g_type_class_ref (client_factory->get_type ());
+	klass = g_type_class_ref (gtype);
 
 	nm_assert (NM_IS_DHCP_CLIENT_CLASS (klass));
 
 	if (addr_family == AF_INET6) {
-		return   klass->ip6_start
-		       ? client_factory
-		       : &_nm_dhcp_client_factory_internal;
+		if (!klass->ip6_start)
+			gtype = _client_factory_get_gtype (&_nm_dhcp_client_factory_internal, addr_family);
+	} else {
+		if (!klass->ip4_start)
+			gtype = _client_factory_get_gtype (&_nm_dhcp_client_factory_internal, addr_family);
 	}
-	return   klass->ip4_start
-	       ? client_factory
-	       : &_nm_dhcp_client_factory_internal;
+
+out:
+	nm_assert (g_type_is_a (gtype, NM_TYPE_DHCP_CLIENT));
+	nm_assert (({
+	               nm_auto_unref_gtypeclass NMDhcpClientClass *k = g_type_class_ref (gtype);
+
+	                  (addr_family == AF_INET6 && k->ip6_start)
+	               || (addr_family == AF_INET  && k->ip4_start);
+	            }));
+
+	return gtype;
 }
 
 /*****************************************************************************/
@@ -211,10 +213,13 @@ client_start (NMDhcpManager *self,
               const struct in6_addr *ipv6_ll_addr,
               GBytes *dhcp_client_id,
               gboolean enforce_duid,
+              guint32 iaid,
+              gboolean iaid_explicit,
               guint32 timeout,
               const char *dhcp_anycast_addr,
               const char *hostname,
               gboolean hostname_use_fqdn,
+              NMDhcpHostnameFlags hostname_flags,
               gboolean info_only,
               NMSettingIP6ConfigPrivacy privacy,
               const char *last_ip4_address,
@@ -225,7 +230,7 @@ client_start (NMDhcpManager *self,
 	NMDhcpClient *client;
 	gboolean success = FALSE;
 	gsize hwaddr_len;
-	const NMDhcpClientFactory *client_factory;
+	GType gtype;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
 	g_return_val_if_fail (iface, NULL);
@@ -251,12 +256,21 @@ client_start (NMDhcpManager *self,
 		g_return_val_if_reached (NULL) ;
 	}
 
+	if (hostname) {
+		if (   (hostname_use_fqdn && !nm_sd_dns_name_is_valid (hostname))
+		    || (!hostname_use_fqdn && !nm_sd_hostname_is_valid (hostname, FALSE))) {
+			nm_log_warn (LOGD_DHCP , "dhcp%c: %s '%s' is invalid, will be ignored",
+			             nm_utils_addr_family_to_char (addr_family),
+			             hostname_use_fqdn ? "FQDN" : "hostname",
+			             hostname);
+			hostname = NULL;
+		}
+	}
+
 	nm_assert (g_bytes_get_size (hwaddr) == g_bytes_get_size (bcast_hwaddr));
 
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
-	client_factory = _client_factory_get_effective (priv->client_factory, addr_family);
-
 	/* Kill any old client instance */
 	client = get_client_for_ifindex (self, addr_family, ifindex);
 	if (client) {
@@ -271,7 +285,14 @@ client_start (NMDhcpManager *self,
 		g_object_unref (client);
 	}
 
-	client = g_object_new (client_factory->get_type (),
+	gtype = _client_factory_get_gtype (priv->client_factory, addr_family);
+
+	nm_log_trace (LOGD_DHCP , "dhcp%c: creating IPv%c DHCP client of type %s",
+	              nm_utils_addr_family_to_char (addr_family),
+	              nm_utils_addr_family_to_char (addr_family),
+	              g_type_name (gtype));
+
+	client = g_object_new (gtype,
 	                       NM_DHCP_CLIENT_MULTI_IDX, multi_idx,
 	                       NM_DHCP_CLIENT_ADDR_FAMILY, addr_family,
 	                       NM_DHCP_CLIENT_INTERFACE, iface,
@@ -279,10 +300,13 @@ client_start (NMDhcpManager *self,
 	                       NM_DHCP_CLIENT_HWADDR, hwaddr,
 	                       NM_DHCP_CLIENT_BROADCAST_HWADDR, bcast_hwaddr,
 	                       NM_DHCP_CLIENT_UUID, uuid,
+	                       NM_DHCP_CLIENT_IAID, (guint) iaid,
+	                       NM_DHCP_CLIENT_IAID_EXPLICIT, iaid_explicit,
 	                       NM_DHCP_CLIENT_HOSTNAME, hostname,
 	                       NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table,
 	                       NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric,
 	                       NM_DHCP_CLIENT_TIMEOUT, (guint) timeout,
+	                       NM_DHCP_CLIENT_HOSTNAME_FLAGS, (guint) hostname_flags,
 	                       NM_DHCP_CLIENT_FLAGS, (guint) (0
 	                           | (hostname_use_fqdn ? NM_DHCP_CLIENT_FLAGS_USE_FQDN  : 0)
 	                           | (info_only         ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY : 0)
@@ -357,6 +381,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
                            gboolean send_hostname,
                            const char *dhcp_hostname,
                            const char *dhcp_fqdn,
+                           NMDhcpHostnameFlags hostname_flags,
                            GBytes *dhcp_client_id,
                            guint32 timeout,
                            const char *dhcp_anycast_addr,
@@ -408,10 +433,13 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
 	                     NULL,
 	                     dhcp_client_id,
 	                     FALSE,
+	                     0,
+	                     FALSE,
 	                     timeout,
 	                     dhcp_anycast_addr,
 	                     hostname,
 	                     use_fqdn,
+	                     hostname_flags,
 	                     FALSE,
 	                     0,
 	                     last_ip_address,
@@ -433,8 +461,11 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            guint32 route_metric,
                            gboolean send_hostname,
                            const char *dhcp_hostname,
+                           NMDhcpHostnameFlags hostname_flags,
                            GBytes *duid,
                            gboolean enforce_duid,
+                           guint32 iaid,
+                           gboolean iaid_explicit,
                            guint32 timeout,
                            const char *dhcp_anycast_addr,
                            gboolean info_only,
@@ -465,10 +496,13 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
 	                     ll_addr,
 	                     duid,
 	                     enforce_duid,
+	                     iaid,
+	                     iaid_explicit,
 	                     timeout,
 	                     dhcp_anycast_addr,
 	                     hostname,
 	                     TRUE,
+	                     hostname_flags,
 	                     info_only,
 	                     privacy,
 	                     NULL,
@@ -529,9 +563,10 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 		if (!f)
 			continue;
 
-		nm_log_dbg (LOGD_DHCP, "dhcp-init: enabled DHCP client '%s' (%s)%s",
-		            f->name, g_type_name (f->get_type ()),
-		            _client_factory_available (f) ? "" : " (not available)");
+		nm_log_dbg (LOGD_DHCP, "dhcp-init: enabled DHCP client '%s'%s%s",
+		            f->name,
+		            _client_factory_available (f) ? "" : " (not available)",
+		            f->experimental ? " (undocumented internal plugin)" : "");
 	}
 
 	/* Client-specific setup */
diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h
index ff0d6f54..fb1c9834 100644
--- a/src/dhcp/nm-dhcp-manager.h
+++ b/src/dhcp/nm-dhcp-manager.h
@@ -1,19 +1,5 @@
-/* nm-dhcp-manager.c - Handle the DHCP daemon for 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, 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -56,6 +42,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip4     (NMDhcpManager *manager,
                                               gboolean send_hostname,
                                               const char *dhcp_hostname,
                                               const char *dhcp_fqdn,
+                                              NMDhcpHostnameFlags hostname_flags,
                                               GBytes *dhcp_client_id,
                                               guint32 timeout,
                                               const char *dhcp_anycast_addr,
@@ -74,8 +61,11 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               guint32 route_metric,
                                               gboolean send_hostname,
                                               const char *dhcp_hostname,
+                                              NMDhcpHostnameFlags hostname_flags,
                                               GBytes *duid,
                                               gboolean enforce_duid,
+                                              guint32 iaid,
+                                              gboolean iaid_explicit,
                                               guint32 timeout,
                                               const char *dhcp_anycast_addr,
                                               gboolean info_only,
@@ -86,7 +76,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
 /* For testing only */
 extern const char* nm_dhcp_helper_path;
 
-extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5];
+extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6];
 
 void nmtst_dhcp_manager_unget (gpointer singleton_instance);
 
diff --git a/src/dhcp/nm-dhcp-nettools.c b/src/dhcp/nm-dhcp-nettools.c
index a71a7a13..e557c004 100644
--- a/src/dhcp/nm-dhcp-nettools.c
+++ b/src/dhcp/nm-dhcp-nettools.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2014-2019 Red Hat, Inc.
+ * Copyright (C) 2014 - 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -42,6 +29,7 @@
 #include "nm-dhcp-client-logging.h"
 #include "n-dhcp4/src/n-dhcp4.h"
 #include "systemd/nm-sd-utils-shared.h"
+#include "systemd/nm-sd-utils-dhcp.h"
 
 /*****************************************************************************/
 
@@ -55,8 +43,6 @@
 typedef struct _NMDhcpNettools NMDhcpNettools;
 typedef struct _NMDhcpNettoolsClass NMDhcpNettoolsClass;
 
-static GType nm_dhcp_nettools_get_type (void);
-
 /*****************************************************************************/
 
 typedef struct {
@@ -65,6 +51,7 @@ typedef struct {
 	NDhcp4ClientLease *lease;
 	GIOChannel *channel;
 	guint event_id;
+	char *lease_file;
 } NMDhcpNettoolsPrivate;
 
 struct _NMDhcpNettools {
@@ -85,13 +72,6 @@ G_DEFINE_TYPE (NMDhcpNettools, nm_dhcp_nettools, NM_TYPE_DHCP_CLIENT)
 #define DHCP_MAX_FQDN_LENGTH 255
 
 enum {
-	DHCP_FQDN_FLAG_S = (1 << 0),
-	DHCP_FQDN_FLAG_O = (1 << 1),
-	DHCP_FQDN_FLAG_E = (1 << 2),
-	DHCP_FQDN_FLAG_N = (1 << 3),
-};
-
-enum {
 	NM_IN_ADDR_CLASS_A,
 	NM_IN_ADDR_CLASS_B,
 	NM_IN_ADDR_CLASS_C,
@@ -354,28 +334,21 @@ lease_get_u16 (NDhcp4ClientLease *lease,
 	return TRUE;
 }
 
-#define LOG_LEASE(domain, ...) \
-    G_STMT_START { \
-        _LOG2I ((domain), (iface), "  "__VA_ARGS__); \
-    } G_STMT_END
-
 static gboolean
 lease_parse_address (NDhcp4ClientLease *lease,
-                     const char *iface,
                      NMIP4Config *ip4_config,
                      GHashTable *options,
                      GError **error)
 {
 	char addr_str[NM_UTILS_INET_ADDRSTRLEN];
-	const gint64 ts = nm_utils_get_monotonic_timestamp_ns ();
-	const gint64 ts_clock_boottime = nm_utils_monotonic_timestamp_as_boottime (ts, 1);
 	struct in_addr a_address;
 	struct in_addr a_netmask;
 	struct in_addr a_next_server;
 	guint32 a_plen;
 	guint64 nettools_lifetime;
-	gint64 a_lifetime;
-	gint64 a_expiry;
+	guint32 a_lifetime;
+	guint32 a_timestamp;
+	guint64 a_expiry;
 
 	n_dhcp4_client_lease_get_yiaddr (lease, &a_address);
 	if (a_address.s_addr == INADDR_ANY) {
@@ -383,29 +356,47 @@ lease_parse_address (NDhcp4ClientLease *lease,
 		return FALSE;
 	}
 
-	/* n_dhcp4_client_lease_get_lifetime() never fails */
 	n_dhcp4_client_lease_get_lifetime (lease, &nettools_lifetime);
-	/* FIXME: n_dhcp4_client_lease_get_lifetime() returns the time in nsec of CLOCK_BOOTTIME.
-	 * We want to retrieve the original lifetime value in seconds, so we approximate it in a_lifetime.
-	 * Use a nettools API to retrieve the original value as passed by the server.
-	 */
+
 	if (nettools_lifetime == G_MAXUINT64) {
+		a_timestamp = 0;
 		a_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
-		a_expiry = NM_PLATFORM_LIFETIME_PERMANENT;
+		a_expiry = G_MAXUINT64;
 	} else {
-		gint64 ts_time = time (NULL);
-
-		a_lifetime = ((gint64) nettools_lifetime - ts_clock_boottime) / NM_UTILS_NS_PER_SECOND;
-		/* A lease time of 0 is allowed on some dhcp servers, so, let's accept it. */
-		if (a_lifetime < 0)
-			a_lifetime = 0;
-		else if (a_lifetime > NM_PLATFORM_LIFETIME_PERMANENT)
-			a_lifetime = NM_PLATFORM_LIFETIME_PERMANENT - 1;
-
-		if (ts_time > NM_PLATFORM_LIFETIME_PERMANENT - a_lifetime)
-			a_expiry = NM_PLATFORM_LIFETIME_PERMANENT - 1;
-		else
-			a_expiry = ts_time + a_lifetime;
+		guint64 nettools_basetime;
+		guint64 lifetime;
+		gint64 ts;
+
+		n_dhcp4_client_lease_get_basetime (lease, &nettools_basetime);
+
+		/* usually we shouldn't assert against external libraries like n-dhcp4.
+		 * Here we still do it... it seems safe enough. */
+		nm_assert (nettools_basetime > 0);
+		nm_assert (nettools_lifetime >= nettools_basetime);
+		nm_assert (((nettools_lifetime - nettools_basetime) % NM_UTILS_NS_PER_SECOND) == 0);
+		nm_assert ((nettools_lifetime - nettools_basetime) / NM_UTILS_NS_PER_SECOND <= G_MAXUINT32);
+
+		if (nettools_lifetime <= nettools_basetime) {
+			/* A lease time of 0 is allowed on some dhcp servers, so, let's accept it. */
+			lifetime = 0;
+		} else {
+			lifetime = nettools_lifetime - nettools_basetime;
+
+			/* we "ceil" the value to the next second. In practice, we don't expect any sub-second values
+			 * from n-dhcp4 anyway, so this should have no effect. */
+			lifetime += NM_UTILS_NS_PER_SECOND - 1;
+		}
+
+		ts = nm_utils_monotonic_timestamp_from_boottime (nettools_basetime, 1);
+
+		/* the timestamp must be positive, because we only started nettools DHCP client
+		 * after obtaining the first monotonic timestamp. Hence, the lease must have been
+		 * received afterwards. */
+		nm_assert (ts >= NM_UTILS_NS_PER_SECOND);
+
+		a_timestamp = ts / NM_UTILS_NS_PER_SECOND;
+		a_lifetime = NM_MIN (lifetime / NM_UTILS_NS_PER_SECOND, NM_PLATFORM_LIFETIME_PERMANENT - 1);
+		a_expiry = time (NULL) + ((lifetime - (nm_utils_clock_gettime_ns (CLOCK_BOOTTIME) - nettools_basetime)) / NM_UTILS_NS_PER_SECOND);
 	}
 
 	if (!lease_get_in_addr (lease, NM_DHCP_OPTION_DHCP4_SUBNET_MASK, &a_netmask)) {
@@ -416,7 +407,6 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	nm_utils_inet4_ntop (a_address.s_addr, addr_str);
 	a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr);
 
-	LOG_LEASE (LOGD_DHCP4, "address %s/%u", addr_str, a_plen);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS,
@@ -426,20 +416,17 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	                           NM_DHCP_OPTION_DHCP4_SUBNET_MASK,
 	                           nm_utils_inet4_ntop (a_netmask.s_addr, addr_str));
 
-	LOG_LEASE (LOGD_DHCP4, "%s '%u' seconds (at %lld)",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options,
-	                                          NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME),
-	           (guint) a_lifetime,
-	           (long long) a_expiry);
 	nm_dhcp_option_add_option_u64 (options,
 	                               _nm_dhcp_option_dhcp4_options,
 	                               NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME,
 	                               (guint64) a_lifetime);
 
-	nm_dhcp_option_add_option_u64 (options,
-	                               _nm_dhcp_option_dhcp4_options,
-	                               NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
-	                               (guint64) a_expiry);
+	if (a_expiry != G_MAXUINT64) {
+		nm_dhcp_option_add_option_u64 (options,
+		                               _nm_dhcp_option_dhcp4_options,
+		                               NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
+		                               a_expiry);
+	}
 
 
 	n_dhcp4_client_lease_get_siaddr (lease, &a_next_server);
@@ -457,7 +444,7 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	                                   .peer_address = a_address.s_addr,
 	                                   .plen         = a_plen,
 	                                   .addr_source  = NM_IP_CONFIG_SOURCE_DHCP,
-	                                   .timestamp    = ts / NM_UTILS_NS_PER_SECOND,
+	                                   .timestamp    = a_timestamp,
 	                                   .lifetime     = a_lifetime,
 	                                   .preferred    = a_lifetime,
 	                           }));
@@ -467,7 +454,6 @@ lease_parse_address (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_domain_name_servers (NDhcp4ClientLease *lease,
-                                 const char *iface,
                                  NMIP4Config *ip4_config,
                                  GHashTable *options)
 {
@@ -498,7 +484,6 @@ lease_parse_domain_name_servers (NDhcp4ClientLease *lease,
 		nm_ip4_config_add_nameserver (ip4_config, addr.s_addr);
 	}
 
-	LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", str->str);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER,
@@ -507,7 +492,6 @@ lease_parse_domain_name_servers (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_routes (NDhcp4ClientLease *lease,
-                    const char *iface,
                     NMIP4Config *ip4_config,
                     GHashTable *options,
                     guint32 route_table,
@@ -537,11 +521,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 			nm_utils_inet4_ntop (dest.s_addr, dest_str);
 			nm_utils_inet4_ntop (gateway.s_addr, gateway_str);
 
-			LOG_LEASE (LOGD_DHCP4,
-			           "classless static route %s/%d gw %s",
-			           dest_str,
-			           (int) plen,
-			           gateway_str);
 			g_string_append_printf (nm_gstring_add_space_delimiter (str),
 			                        "%s/%d %s",
 			                        dest_str,
@@ -586,11 +565,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 			nm_utils_inet4_ntop (dest.s_addr, dest_str);
 			nm_utils_inet4_ntop (gateway.s_addr, gateway_str);
 
-			LOG_LEASE (LOGD_DHCP4,
-			           "static route %s/%d gw %s",
-			           dest_str,
-			           (int) plen,
-			           gateway_str);
 			g_string_append_printf (nm_gstring_add_space_delimiter (str),
 			                        "%s/%d %s",
 			                        dest_str,
@@ -667,7 +641,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 			                         }),
 			                         NULL);
 		}
-		LOG_LEASE (LOGD_DHCP4, "router %s", str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_ROUTER,
@@ -677,7 +650,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_mtu (NDhcp4ClientLease *lease,
-                 const char *iface,
                  NMIP4Config *ip4_config,
                  GHashTable *options)
 {
@@ -689,7 +661,6 @@ lease_parse_mtu (NDhcp4ClientLease *lease,
 	if (mtu < 68)
 		return;
 
-	LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu);
 	nm_dhcp_option_add_option_u64 (options,
 	                               _nm_dhcp_option_dhcp4_options,
 	                               NM_DHCP_OPTION_DHCP4_INTERFACE_MTU,
@@ -699,7 +670,6 @@ lease_parse_mtu (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_metered (NDhcp4ClientLease *lease,
-                     const char *iface,
                      NMIP4Config *ip4_config,
                      GHashTable *options)
 {
@@ -715,13 +685,12 @@ lease_parse_metered (NDhcp4ClientLease *lease,
 		metered = !!memmem (data, n_data, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED"));
 	}
 
-	LOG_LEASE (LOGD_DHCP4, "%s", metered ? "metered" : "unmetered");
+	/* TODO: expose the vendor specific option when present */
 	nm_ip4_config_set_metered (ip4_config, metered);
 }
 
 static void
 lease_parse_ntps (NDhcp4ClientLease *lease,
-                  const char *iface,
                   GHashTable *options)
 {
 	nm_auto_free_gstring GString *str = NULL;
@@ -742,13 +711,14 @@ lease_parse_ntps (NDhcp4ClientLease *lease,
 		g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
 	}
 
-	LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_NTP_SERVER,
+	                           str->str);
 }
 
 static void
 lease_parse_hostname (NDhcp4ClientLease *lease,
-                      const char *iface,
                       GHashTable *options)
 {
 	nm_auto_free_gstring GString *str = NULL;
@@ -765,13 +735,14 @@ lease_parse_hostname (NDhcp4ClientLease *lease,
 	if (is_localhost(str->str))
 		return;
 
-	LOG_LEASE (LOGD_DHCP4, "hostname '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_HOST_NAME,
+	                           str->str);
 }
 
 static void
 lease_parse_domainname (NDhcp4ClientLease *lease,
-                        const char *iface,
                         NMIP4Config *ip4_config,
                         GHashTable *options)
 {
@@ -798,13 +769,14 @@ lease_parse_domainname (NDhcp4ClientLease *lease,
 		g_string_append (nm_gstring_add_space_delimiter (str), *d);
 		nm_ip4_config_add_domain (ip4_config, *d);
 	}
-	LOG_LEASE (LOGD_DHCP4, "domain name '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME,
+	                           str->str);
 }
 
 static void
 lease_parse_search_domains (NDhcp4ClientLease *lease,
-                            const char *iface,
                             NMIP4Config *ip4_config,
                             GHashTable *options)
 {
@@ -832,7 +804,6 @@ lease_parse_search_domains (NDhcp4ClientLease *lease,
 		g_string_append (nm_gstring_add_space_delimiter (str), domain->str);
 		nm_ip4_config_add_search (ip4_config, domain->str);
 	}
-	LOG_LEASE (LOGD_DHCP4, "domain search '%s'", str->str);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST,
@@ -841,7 +812,6 @@ lease_parse_search_domains (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_root_path (NDhcp4ClientLease *lease,
-                       const char *iface,
                        GHashTable *options)
 {
 	nm_auto_free_gstring GString *str = NULL;
@@ -854,16 +824,17 @@ lease_parse_root_path (NDhcp4ClientLease *lease,
 		return;
 
 	str = g_string_new_len ((char *)data, n_data);
-	LOG_LEASE (LOGD_DHCP4, "root path '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_ROOT_PATH,
+	                           str->str);
 }
 
 static void
 lease_parse_wpad (NDhcp4ClientLease *lease,
-                  const char *iface,
                   GHashTable *options)
 {
-	nm_auto_free_gstring GString *str = NULL;
+	gs_free char *wpad = NULL;
 	uint8_t *data;
 	size_t n_data;
 	int r;
@@ -872,12 +843,44 @@ lease_parse_wpad (NDhcp4ClientLease *lease,
 	if (r)
 		return;
 
-	str = g_string_new_len ((char *)data, n_data);
-	LOG_LEASE (LOGD_DHCP4, "wpad '%s'", str->str);
+	nm_utils_buf_utf8safe_escape ((char *)data, n_data, 0, &wpad);
+	if (wpad == NULL)
+		wpad = g_strndup ((char *)data, n_data);
+
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY,
-	                           str->str);
+	                           wpad);
+}
+
+static void
+lease_parse_private_options (NDhcp4ClientLease *lease,
+                             GHashTable *options)
+{
+	int i;
+
+	for (i = NM_DHCP_OPTION_DHCP4_PRIVATE_224; i <= NM_DHCP_OPTION_DHCP4_PRIVATE_254; i++) {
+		gs_free char *option_string = NULL;
+		guint8 *data;
+		gsize n_data;
+		int r;
+
+		/* We manage private options 249 (private classless static route) and 252 (wpad) in a special
+		 * way, so skip them as we here just manage all (the other) private options as raw data */
+		if (NM_IN_SET (i, NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE,
+		                  NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY))
+			continue;
+
+		r = n_dhcp4_client_lease_query (lease, i, &data, &n_data);
+		if (r)
+			continue;
+
+		option_string = nm_utils_bin2hexstr_full (data, n_data, ':', FALSE, NULL);
+		nm_dhcp_option_take_option (options,
+		                            _nm_dhcp_option_dhcp4_options,
+		                            i,
+		                            g_steal_pointer (&option_string));
+	}
 }
 
 static NMIP4Config *
@@ -896,22 +899,23 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	g_return_val_if_fail (lease != NULL, NULL);
 
 	ip4_config = nm_ip4_config_new (multi_idx, ifindex);
-	options = out_options ? nm_dhcp_option_create_options_dict () : NULL;
+	options = nm_dhcp_option_create_options_dict ();
 
-	if (!lease_parse_address (lease, iface, ip4_config, options, error))
+	if (!lease_parse_address (lease, ip4_config, options, error))
 		return NULL;
 
-	lease_parse_routes (lease, iface, ip4_config, options, route_table, route_metric);
-	lease_parse_domain_name_servers (lease, iface, ip4_config, options);
-	lease_parse_domainname (lease, iface, ip4_config, options);
-	lease_parse_search_domains (lease, iface, ip4_config, options);
-	lease_parse_mtu (lease, iface, ip4_config, options);
-	lease_parse_metered (lease, iface, ip4_config, options);
+	lease_parse_routes (lease, ip4_config, options, route_table, route_metric);
+	lease_parse_domain_name_servers (lease, ip4_config, options);
+	lease_parse_domainname (lease, ip4_config, options);
+	lease_parse_search_domains (lease, ip4_config, options);
+	lease_parse_mtu (lease, ip4_config, options);
+	lease_parse_metered (lease, ip4_config, options);
 
-	lease_parse_hostname (lease, iface, options);
-	lease_parse_ntps (lease, iface, options);
-	lease_parse_root_path (lease, iface, options);
-	lease_parse_wpad (lease, iface, options);
+	lease_parse_hostname (lease, options);
+	lease_parse_ntps (lease, options);
+	lease_parse_root_path (lease, options);
+	lease_parse_wpad (lease, options);
+	lease_parse_private_options (lease, options);
 
 	NM_SET_OUT (out_options, g_steal_pointer (&options));
 	return g_steal_pointer (&ip4_config);
@@ -920,8 +924,34 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 /*****************************************************************************/
 
 static void
+lease_save (NDhcp4ClientLease *lease, const char *lease_file)
+{
+	struct in_addr a_address;
+	nm_auto_free_gstring GString *new_contents = NULL;
+	char sbuf[NM_UTILS_INET_ADDRSTRLEN];
+
+	nm_assert (lease);
+	nm_assert (lease_file);
+
+	new_contents = g_string_new ("# This is private data. Do not parse.\n");
+
+	n_dhcp4_client_lease_get_yiaddr (lease, &a_address);
+	if (a_address.s_addr == INADDR_ANY)
+		return;
+
+	g_string_append_printf (new_contents,
+	                        "ADDRESS=%s\n", nm_utils_inet4_ntop (a_address.s_addr, sbuf));
+
+	g_file_set_contents (lease_file,
+	                     new_contents->str,
+	                     -1,
+	                     NULL);
+}
+
+static void
 bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease)
 {
+	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 	gs_unref_object NMIP4Config *ip4_config = NULL;
 	gs_unref_hashtable GHashTable *options = NULL;
@@ -945,6 +975,7 @@ bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease)
 	}
 
 	nm_dhcp_option_add_requests_to_options (options, _nm_dhcp_option_dhcp4_options);
+	lease_save (lease, priv->lease_file);
 
 	nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
 	                          NM_DHCP_STATE_BOUND,
@@ -1015,6 +1046,27 @@ dhcp4_event_cb (GIOChannel *source,
 	return G_SOURCE_CONTINUE;
 }
 
+G_GNUC_PRINTF (3, 4)
+static void
+nettools_log (int level, void *data, const char *fmt, ...)
+{
+	NMDhcpNettools *self = data;
+	NMLogLevel nm_level;
+	gs_free char *msg = NULL;
+	va_list ap;
+
+	nm_level = nm_log_level_from_syslog (level);
+	if (nm_logging_enabled (nm_level, LOGD_DHCP4)) {
+		va_start (ap, fmt);
+		msg = g_strdup_vprintf (fmt, ap);
+		va_end (ap);
+		nm_log (nm_level, LOGD_DHCP4, NULL , NULL,
+		        "dhcp4 (%s): %s",
+		        nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)),
+		        msg);
+	}
+}
+
 static gboolean
 nettools_create (NMDhcpNettools *self,
                  const char *dhcp_anycast_addr,
@@ -1084,11 +1136,15 @@ nettools_create (NMDhcpNettools *self,
 		return FALSE;
 	}
 
+	n_dhcp4_client_config_set_log_level (config, nm_log_level_to_syslog (nm_logging_get_level (LOGD_DHCP4)));
+	n_dhcp4_client_config_set_log_func (config, nettools_log, self);
 	n_dhcp4_client_config_set_ifindex (config, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)));
 	n_dhcp4_client_config_set_transport (config, transport);
 	n_dhcp4_client_config_set_mac (config, hwaddr_arr, hwaddr_len);
 	n_dhcp4_client_config_set_broadcast_mac (config, bcast_hwaddr_arr, bcast_hwaddr_len);
-	r = n_dhcp4_client_config_set_client_id (config, client_id_arr, client_id_len);
+	r = n_dhcp4_client_config_set_client_id (config,
+	                                         client_id_arr,
+	                                         NM_MIN (client_id_len, 1 + _NM_SD_MAX_CLIENT_ID_LEN));
 	if (r) {
 		nm_utils_error_set_errno (error, r, "failed to set client-id: %s");
 		return FALSE;
@@ -1157,6 +1213,22 @@ decline (NMDhcpClient *client,
 	return TRUE;
 }
 
+static guint8
+fqdn_flags_to_wire (NMDhcpHostnameFlags flags)
+{
+	guint r = 0;
+
+	/* RFC 4702 section 2.1 */
+	if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)
+		r |= (1 << 0);
+	if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED)
+		r |= (1 << 2);
+	if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)
+		r |= (1 << 3);
+
+	return r;
+}
+
 static gboolean
 ip4_start (NMDhcpClient *client,
            const char *dhcp_anycast_addr,
@@ -1166,6 +1238,7 @@ ip4_start (NMDhcpClient *client,
 	nm_auto (n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL;
 	NMDhcpNettools *self = NM_DHCP_NETTOOLS (client);
 	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self);
+	gs_free char *lease_file = NULL;
 	struct in_addr last_addr = { 0 };
 	const char *hostname;
 	int r, i;
@@ -1187,11 +1260,32 @@ ip4_start (NMDhcpClient *client,
 	 */
 	n_dhcp4_client_probe_config_set_start_delay (config, 1);
 
-	if (last_ip4_address) {
+	nm_dhcp_utils_get_leasefile_path (AF_INET,
+	                                  "internal",
+	                                  nm_dhcp_client_get_iface (client),
+	                                  nm_dhcp_client_get_uuid (client),
+	                                  &lease_file);
+
+	if (last_ip4_address)
 		inet_pton (AF_INET, last_ip4_address, &last_addr);
-		n_dhcp4_client_probe_config_set_requested_ip (config, last_addr);
+	else {
+		/*
+		 * TODO: we stick to the systemd-networkd lease file format. Quite easy for now to
+		 * just use the functions in systemd code. Anyway, as in the end we just use the
+		 * ip address from all the options found in the lease, write a function that parses
+		 * the lease file just for the assigned address and returns it in &last_address.
+		 * Then drop reference to systemd-networkd structures and functions.
+		 */
+		nm_auto (sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+
+		dhcp_lease_load (&lease, lease_file);
+		if (lease)
+			sd_dhcp_lease_get_address (lease, &last_addr);
 	}
 
+	if (last_addr.s_addr)
+		n_dhcp4_client_probe_config_set_requested_ip (config, last_addr);
+
 	/* Add requested options */
 	for (i = 0; _nm_dhcp_option_dhcp4_options[i].name; i++) {
 		if (_nm_dhcp_option_dhcp4_options[i].include) {
@@ -1204,26 +1298,38 @@ ip4_start (NMDhcpClient *client,
 	hostname = nm_dhcp_client_get_hostname (client);
 	if (hostname) {
 		if (nm_dhcp_client_get_use_fqdn (client)) {
-			uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
-
-			buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
-			            DHCP_FQDN_FLAG_E;  /* Canonical wire format */
-			buffer[1] = 0;                 /* RCODE1 (deprecated) */
-			buffer[2] = 0;                 /* RCODE2 (deprecated) */
-
-			r = nm_sd_dns_name_to_wire_format (hostname,
-			                                   buffer + 3,
-			                                   sizeof (buffer) - 3,
-			                                   FALSE);
-			if (r < 0) {
-				nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s");
-				return FALSE;
+			uint8_t buffer[255];
+			NMDhcpHostnameFlags flags;
+			size_t fqdn_len;
+
+			flags = nm_dhcp_client_get_hostname_flags (client);
+			buffer[0] = fqdn_flags_to_wire (flags);
+			buffer[1] = 0;   /* RCODE1 (deprecated) */
+			buffer[2] = 0;   /* RCODE2 (deprecated) */
+
+			if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED) {
+				r = nm_sd_dns_name_to_wire_format (hostname,
+				                                   buffer + 3,
+				                                   sizeof (buffer) - 3,
+				                                   FALSE);
+				if (r <= 0) {
+					nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s");
+					return FALSE;
+				}
+				fqdn_len = r;
+			} else {
+				fqdn_len = strlen (hostname);
+				if (fqdn_len > sizeof (buffer) - 3) {
+					nm_utils_error_set (error, r, "failed to set DHCP FQDN: name too long");
+					return FALSE;
+				}
+				memcpy (buffer + 3, hostname, fqdn_len);
 			}
 
 			r = n_dhcp4_client_probe_config_append_option (config,
 			                                               NM_DHCP_OPTION_DHCP4_CLIENT_FQDN,
 			                                               buffer,
-			                                               3 + r);
+			                                               3 + fqdn_len);
 			if (r) {
 				nm_utils_error_set_errno (error, r, "failed to set DHCP FQDN: %s");
 				return FALSE;
@@ -1240,6 +1346,9 @@ ip4_start (NMDhcpClient *client,
 		}
 	}
 
+	g_free (priv->lease_file);
+	priv->lease_file = g_steal_pointer (&lease_file);
+
 	r = n_dhcp4_client_probe (priv->client, &priv->probe, config);
 	if (r) {
 		nm_utils_error_set_errno (error, r, "failed to start DHCP client: %s");
@@ -1279,6 +1388,7 @@ dispose (GObject *object)
 {
 	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE ((NMDhcpNettools *) object);
 
+	nm_clear_pointer (&priv->lease_file, g_free);
 	nm_clear_pointer (&priv->channel, g_io_channel_unref);
 	nm_clear_g_source (&priv->event_id);
 	nm_clear_pointer (&priv->lease, n_dhcp4_client_lease_unref);
@@ -1303,7 +1413,7 @@ nm_dhcp_nettools_class_init (NMDhcpNettoolsClass *class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_nettools = {
-	.name = "nettools",
-	.get_type = nm_dhcp_nettools_get_type,
-	.get_path = NULL,
+	.name         = "nettools",
+	.get_type     = nm_dhcp_nettools_get_type,
+	.experimental = TRUE,
 };
diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c
index 22e7d90e..4c003f31 100644
--- a/src/dhcp/nm-dhcp-options.c
+++ b/src/dhcp/nm-dhcp-options.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -237,6 +223,7 @@ nm_dhcp_option_take_option (GHashTable *options,
 	nm_assert (options);
 	nm_assert (requests);
 	nm_assert (value);
+	nm_assert (g_utf8_validate (value, -1, NULL));
 
 	g_hash_table_insert (options,
 	                     (gpointer) nm_dhcp_option_request_string (requests, option),
diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h
index f56edb19..bf9ccd57 100644
--- a/src/dhcp/nm-dhcp-options.h
+++ b/src/dhcp/nm-dhcp-options.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_DHCP_OPTIONS_H__
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 96409345..1518d465 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -1,18 +1,5 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -30,7 +17,6 @@
 #include "nm-std-aux/unaligned.h"
 
 #include "nm-utils.h"
-#include "nm-config.h"
 #include "nm-dhcp-utils.h"
 #include "nm-dhcp-options.h"
 #include "nm-core-utils.h"
@@ -81,13 +67,6 @@ G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
 
 /*****************************************************************************/
 
-#define LOG_LEASE(domain, ...) \
-G_STMT_START { \
-	if (log_lease) { \
-		_LOG2D ((domain), (iface), "  "__VA_ARGS__); \
-	} \
-} G_STMT_END
-
 static NMIP4Config *
 lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
                      const char *iface,
@@ -95,7 +74,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
                      sd_dhcp_lease *lease,
                      guint32 route_table,
                      guint32 route_metric,
-                     gboolean log_lease,
                      GHashTable **out_options,
                      GError **error)
 {
@@ -151,25 +129,17 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	options = out_options ? nm_dhcp_option_create_options_dict () : NULL;
 
 	nm_utils_inet4_ntop (a_address.s_addr, addr_str);
-	LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS),
-	           addr_str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, addr_str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS,
+	                           addr_str);
 
 	a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr);
-	LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SUBNET_MASK),
-	           (guint) a_plen);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_SUBNET_MASK,
 	                           nm_utils_inet4_ntop (a_netmask.s_addr, addr_str));
 
-	LOG_LEASE (LOGD_DHCP4, "%s '%u' seconds (at %lld)",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options,
-	                                          NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME),
-	           (guint) a_lifetime,
-	           (long long) (ts_time + a_lifetime));
 	nm_dhcp_option_add_option_u64 (options,
 	                               _nm_dhcp_option_dhcp4_options,
 	                               NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME,
@@ -200,9 +170,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 
 	if (sd_dhcp_lease_get_server_identifier (lease, &server_id) >= 0) {
 		nm_utils_inet4_ntop (server_id.s_addr, addr_str);
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SERVER_ID),
-		           addr_str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_SERVER_ID,
@@ -211,9 +178,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 
 	if (sd_dhcp_lease_get_broadcast (lease, &broadcast) >= 0) {
 		nm_utils_inet4_ntop (broadcast.s_addr, addr_str);
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_BROADCAST),
-		           addr_str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_BROADCAST,
@@ -235,9 +199,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			}
 			nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER,
@@ -251,9 +212,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			g_string_append (nm_gstring_add_space_delimiter (str), search_domains[i]);
 			nm_ip4_config_add_search (ip4_config, search_domains[i]);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST,
@@ -264,10 +222,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 		gs_strfreev char **domains = NULL;
 		char **d;
 
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME),
-		           s);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, s);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME,
+		                           s);
 
 		/* Multiple domains sometimes stuffed into option 15 "Domain Name".
 		 * As systemd escapes such characters, split them at \\032. */
@@ -277,10 +235,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_hostname (lease, &s) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME),
-		           s);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, s);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_HOST_NAME,
+		                           s);
 	}
 
 	num = sd_dhcp_lease_get_routes (lease, &routes);
@@ -333,14 +291,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			nm_utils_inet4_ntop (network_net, network_net_str);
 			nm_utils_inet4_ntop (r_gateway.s_addr, gateway_str);
 
-			LOG_LEASE (LOGD_DHCP4,
-			           "%sstatic_route %s/%d gw %s",
-			             option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
-			           ? "rfc3442_classless_"
-			           : "",
-			           network_net_str,
-			           (int) r_plen,
-			           gateway_str);
 			g_string_append_printf (nm_gstring_add_space_delimiter (  option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
 			                                                        ? str_classless
 			                                                        : str_static),
@@ -442,17 +392,14 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			                         }),
 			                         NULL);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER),
-		           str->str);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER, str->str);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_ROUTER,
+		                           str->str);
 	}
 
 	if (   sd_dhcp_lease_get_mtu (lease, &mtu) >= 0
 	    && mtu) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU),
-		           mtu);
 		nm_dhcp_option_add_option_u64 (options,
 		                               _nm_dhcp_option_dhcp4_options,
 		                               NM_DHCP_OPTION_DHCP4_INTERFACE_MTU,
@@ -467,9 +414,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str);
 			g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_NTP_SERVER,
@@ -477,16 +421,13 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_root_path (lease, &s) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH),
-		           s);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, s);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_ROOT_PATH,
+		                           s);
 	}
 
 	if (sd_dhcp_lease_get_t1 (lease, &renewal) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME),
-		           renewal);
 		nm_dhcp_option_add_option_u64 (options,
 		                               _nm_dhcp_option_dhcp4_options,
 		                               NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME,
@@ -494,9 +435,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_t2 (lease, &rebinding) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME),
-		           rebinding);
 		nm_dhcp_option_add_option_u64 (options,
 		                               _nm_dhcp_option_dhcp4_options,
 		                               NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME,
@@ -504,9 +442,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_timezone (lease, &s) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE),
-		           s);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE,
@@ -525,9 +460,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			option_string = nm_utils_bin2hexstr_full (private_options[i].data,
 			                                          private_options[i].data_len,
 			                                          ':', FALSE, NULL);
-			LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-			           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, private_options[i].code),
-			           option_string);
 			if (!options) {
 				g_free (option_string);
 				continue;
@@ -544,37 +476,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 
 /*****************************************************************************/
 
-static char *
-get_leasefile_path (int addr_family, const char *iface, const char *uuid)
-{
-	char *rundir_path;
-	char *statedir_path;
-
-	rundir_path = g_strdup_printf (NMRUNDIR "/internal%s-%s-%s.lease",
-	                               addr_family == AF_INET6 ? "6" : "",
-	                               uuid,
-	                               iface);
-
-	if (g_file_test (rundir_path, G_FILE_TEST_EXISTS))
-		return rundir_path;
-
-	statedir_path = g_strdup_printf (NMSTATEDIR "/internal%s-%s-%s.lease",
-	                                 addr_family == AF_INET6 ? "6" : "",
-	                                 uuid,
-	                                 iface);
-
-	if (   g_file_test (statedir_path, G_FILE_TEST_EXISTS)
-	    || nm_config_get_configure_and_quit (nm_config_get ()) != NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) {
-		g_free (rundir_path);
-		return statedir_path;
-	} else {
-		g_free (statedir_path);
-		return rundir_path;
-	}
-}
-
-/*****************************************************************************/
-
 static void
 bound4_handle (NMDhcpSystemd *self)
 {
@@ -600,7 +501,6 @@ bound4_handle (NMDhcpSystemd *self)
 	                                  lease,
 	                                  nm_dhcp_client_get_route_table (NM_DHCP_CLIENT (self)),
 	                                  nm_dhcp_client_get_route_metric (NM_DHCP_CLIENT (self)),
-	                                  TRUE,
 	                                  &options,
 	                                  &error);
 	if (!ip4_config) {
@@ -713,9 +613,11 @@ ip4_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
-	lease_file = get_leasefile_path (AF_INET,
-	                                 nm_dhcp_client_get_iface (client),
-	                                 nm_dhcp_client_get_uuid (client));
+	nm_dhcp_utils_get_leasefile_path (AF_INET,
+	                                  "internal",
+	                                  nm_dhcp_client_get_iface (client),
+	                                  nm_dhcp_client_get_uuid (client),
+	                                  &lease_file);
 
 	if (last_ip4_address)
 		inet_pton (AF_INET, last_ip4_address, &last_addr);
@@ -814,9 +716,9 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
                      const char *iface,
                      int ifindex,
                      sd_dhcp6_lease *lease,
-                     gboolean log_lease,
                      gboolean info_only,
                      GHashTable **out_options,
+                     gint32 ts,
                      GError **error)
 {
 	gs_unref_object NMIP6Config *ip6_config = NULL;
@@ -827,7 +729,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 	char **domains;
 	nm_auto_free_gstring GString *str = NULL;
 	int num, i;
-	const gint32 ts = nm_utils_get_monotonic_timestamp_s ();
 
 	g_return_val_if_fail (lease, NULL);
 
@@ -838,7 +739,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 	sd_dhcp6_lease_reset_address_iter (lease);
 	nm_gstring_prepare (&str);
 	while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
-		char sbuf[400];
 		const NMPlatformIP6Address address = {
 			.plen        = 128,
 			.address     = tmp_addr,
@@ -852,10 +752,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 
 		nm_utils_inet6_ntop (&tmp_addr, addr_str);
 		g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
-
-		LOG_LEASE (LOGD_DHCP6, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS),
-		           nm_platform_ip6_address_to_string (&address, sbuf, sizeof (sbuf)));
 	};
 	if (str->len)
 		nm_dhcp_option_add_option (options,
@@ -880,9 +776,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 			g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
 			nm_ip6_config_add_nameserver (ip6_config, &dns[i]);
 		}
-		LOG_LEASE (LOGD_DHCP6, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DNS_SERVERS),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp6_options,
 		                           NM_DHCP_OPTION_DHCP6_DNS_SERVERS,
@@ -896,9 +789,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 			g_string_append (nm_gstring_add_space_delimiter (str), domains[i]);
 			nm_ip6_config_add_search (ip6_config, domains[i]);
 		}
-		LOG_LEASE (LOGD_DHCP6, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DOMAIN_LIST),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp6_options,
 		                           NM_DHCP_OPTION_DHCP6_DOMAIN_LIST,
@@ -913,10 +803,12 @@ static void
 bound6_handle (NMDhcpSystemd *self)
 {
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
+	const gint32 ts = nm_utils_get_monotonic_timestamp_s ();
 	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 	gs_unref_object NMIP6Config *ip6_config = NULL;
 	gs_unref_hashtable GHashTable *options = NULL;
 	gs_free_error GError *error = NULL;
+	NMPlatformIP6Address prefix = { 0 };
 	sd_dhcp6_lease *lease;
 
 	if (   sd_dhcp6_client_get_lease (priv->client6, &lease) < 0
@@ -932,9 +824,9 @@ bound6_handle (NMDhcpSystemd *self)
 	                                  iface,
 	                                  nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)),
 	                                  lease,
-	                                  TRUE,
 	                                  nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)),
 	                                  &options,
+	                                  ts,
 	                                  &error);
 
 	if (!ip6_config) {
@@ -947,6 +839,16 @@ bound6_handle (NMDhcpSystemd *self)
 	                          NM_DHCP_STATE_BOUND,
 	                          NM_IP_CONFIG_CAST (ip6_config),
 	                          options);
+
+	sd_dhcp6_lease_reset_pd_prefix_iter (lease);
+	while (!sd_dhcp6_lease_get_pd (lease,
+	                               &prefix.address,
+	                               &prefix.plen,
+	                               &prefix.preferred,
+	                               &prefix.lifetime)) {
+		prefix.timestamp = ts;
+		nm_dhcp_client_emit_ipv6_prefix_delegated (NM_DHCP_CLIENT (self), &prefix);
+	}
 }
 
 static void
@@ -990,7 +892,6 @@ ip6_start (NMDhcpClient *client,
 	nm_auto (sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL;
 	GBytes *hwaddr;
 	const char *hostname;
-	const char *iface;
 	int r, i;
 	const guint8 *duid_arr;
 	gsize duid_len;
@@ -1015,22 +916,13 @@ ip6_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
-	if (needed_prefixes > 0) {
-		_LOGW ("dhcp-client6: prefix delegation not yet supported, won't supply %d prefixes",
-		       needed_prefixes);
-	}
-
 	_LOGT ("dhcp-client6: set %p", sd_client);
 
 	if (nm_dhcp_client_get_info_only (client))
 		sd_dhcp6_client_set_information_request (sd_client, 1);
 
-	iface = nm_dhcp_client_get_iface (client);
-
 	r = sd_dhcp6_client_set_iaid (sd_client,
-	                              nm_utils_create_dhcp_iaid (TRUE,
-	                                                         (const guint8 *) iface,
-	                                                         strlen (iface)));
+	                              nm_dhcp_client_get_iaid (client));
 	if (r < 0) {
 		nm_utils_error_set_errno (error, r, "failed to set IAID: %s");
 		return FALSE;
@@ -1082,6 +974,18 @@ ip6_start (NMDhcpClient *client,
 		}
 	}
 
+	if (needed_prefixes > 0) {
+		if (needed_prefixes > 1)
+			_LOGW ("dhcp-client6: only one prefix request is supported");
+		/* FIXME: systemd-networkd API only allows to request a
+		 * single prefix */
+		r = sd_dhcp6_client_set_prefix_delegation (sd_client, TRUE);
+		if (r < 0) {
+			nm_utils_error_set_errno (error, r, "failed to enable prefix delegation: %s");
+			return FALSE;
+		}
+	}
+
 	r = sd_dhcp6_client_set_local_address (sd_client, ll_addr);
 	if (r < 0) {
 		nm_utils_error_set_errno (error, r, "failed to set local address: %s");
@@ -1182,8 +1086,25 @@ nm_dhcp_systemd_class_init (NMDhcpSystemdClass *sdhcp_class)
 	client_class->stop = stop;
 }
 
+const NMDhcpClientFactory _nm_dhcp_client_factory_systemd = {
+	.name         = "systemd",
+	.get_type     = nm_dhcp_systemd_get_type,
+	.experimental = TRUE,
+};
+
+/*****************************************************************************/
+
+static GType
+_get_type_per_addr_family (int addr_family)
+{
+	nm_assert_addr_family (addr_family);
+
+	if (addr_family == AF_INET)
+		return nm_dhcp_nettools_get_type ();
+	return nm_dhcp_systemd_get_type ();
+}
+
 const NMDhcpClientFactory _nm_dhcp_client_factory_internal = {
-	.name = "internal",
-	.get_type = nm_dhcp_systemd_get_type,
-	.get_path = NULL,
+	.name                     = "internal",
+	.get_type_per_addr_family = _get_type_per_addr_family,
 };
diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c
index 3f9368f2..c5da3e02 100644
--- a/src/dhcp/nm-dhcp-utils.c
+++ b/src/dhcp/nm-dhcp-utils.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -25,6 +12,7 @@
 
 #include "nm-dhcp-utils.h"
 #include "nm-utils.h"
+#include "nm-config.h"
 #include "NetworkManagerUtils.h"
 #include "platform/nm-platform.h"
 #include "nm-dhcp-client-logging.h"
@@ -762,3 +750,55 @@ nm_dhcp_utils_client_id_string_to_bytes (const char *client_id)
 	return bytes;
 }
 
+/**
+ * nm_dhcp_utils_get_leasefile_path:
+ * @addr_family: the IP address family
+ * @plugin_name: the name of the plugin part of the lease file name
+ * @iface: the interface name to which the lease relates to
+ * @uuid: uuid of the connection to which the lease relates to
+ * @out_leasefile_path: will store the computed lease file path
+ *
+ * Constructs the lease file name on the basis of the calling plugin,
+ * interface name and connection uuid. Then returns in @out_leasefile_path
+ * the full path of the lease filename.
+ *
+ * Returns: TRUE if the lease file already exists, FALSE otherwise.
+ */
+gboolean
+nm_dhcp_utils_get_leasefile_path (int addr_family,
+                                  const char *plugin_name,
+                                  const char *iface,
+                                  const char *uuid,
+                                  char **out_leasefile_path)
+{
+	gs_free char *rundir_path = NULL;
+	gs_free char *statedir_path = NULL;
+
+	rundir_path = g_strdup_printf (NMRUNDIR "/%s%s-%s-%s.lease",
+	                               plugin_name,
+	                               addr_family == AF_INET6 ? "6" : "",
+	                               uuid,
+	                               iface);
+
+	if (g_file_test (rundir_path, G_FILE_TEST_EXISTS)) {
+		*out_leasefile_path = g_steal_pointer (&rundir_path);
+		return TRUE;
+	}
+
+	statedir_path = g_strdup_printf (NMSTATEDIR "/%s%s-%s-%s.lease",
+	                                 plugin_name,
+	                                 addr_family == AF_INET6 ? "6" : "",
+	                                 uuid,
+	                                 iface);
+
+	if (g_file_test (statedir_path, G_FILE_TEST_EXISTS)) {
+		*out_leasefile_path = g_steal_pointer (&statedir_path);
+		return TRUE;
+	}
+
+	if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
+		*out_leasefile_path = g_steal_pointer (&rundir_path);
+	else
+		*out_leasefile_path = g_steal_pointer (&statedir_path);
+	return FALSE;
+}
diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h
index 39ae7693..e4c33314 100644
--- a/src/dhcp/nm-dhcp-utils.h
+++ b/src/dhcp/nm-dhcp-utils.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -40,7 +28,13 @@ NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options)
 
 char *nm_dhcp_utils_duid_to_string (GBytes *duid);
 
-GBytes *     nm_dhcp_utils_client_id_string_to_bytes (const char *client_id);
+GBytes *nm_dhcp_utils_client_id_string_to_bytes (const char *client_id);
+
+gboolean nm_dhcp_utils_get_leasefile_path (int addr_family,
+                                           const char *plugin_name,
+                                           const char *iface,
+                                           const char *uuid,
+                                           char **out_leasefile_path);
 
 #endif /* __NETWORKMANAGER_DHCP_UTILS_H__ */
 
diff --git a/src/dhcp/tests/meson.build b/src/dhcp/tests/meson.build
index 43b33951..031e2efd 100644
--- a/src/dhcp/tests/meson.build
+++ b/src/dhcp/tests/meson.build
@@ -7,7 +7,8 @@ foreach test_unit: test_units
   exe = executable(
     test_unit,
     test_unit + '.c',
-    dependencies: test_nm_dep,
+    dependencies: libnetwork_manager_test_dep,
+    c_args: test_c_flags,
   )
 
   test(
diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
index 9e51fceb..761f9cdb 100644
--- a/src/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/dhcp/tests/test-dhcp-dhclient.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -43,6 +29,7 @@ test_config (const char *orig,
              const char *hostname,
              guint32 timeout,
              gboolean use_fqdn,
+             NMDhcpHostnameFlags hostname_flags,
              const char *dhcp_client_id,
              GBytes *expected_new_client_id,
              const char *iface,
@@ -64,6 +51,7 @@ test_config (const char *orig,
 	                                      hostname,
 	                                      timeout,
 	                                      use_fqdn,
+	                                      hostname_flags,
 	                                      "/path/to/dhclient.conf",
 	                                      orig,
 	                                      &new_client_id);
@@ -108,7 +96,11 @@ static const char *orig_missing_expected = \
 static void
 test_orig_missing (void)
 {
-	test_config (NULL, orig_missing_expected, AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", NULL);
+	test_config (NULL,
+	             orig_missing_expected,
+	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
+	             NULL, NULL, "eth0", NULL);
 }
 
 /*****************************************************************************/
@@ -139,6 +131,7 @@ test_override_client_id (void)
 {
 	test_config (override_client_id_orig, override_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "11:22:33:44:55:66",
 	             NULL,
 	             "eth0",
@@ -169,6 +162,7 @@ test_quote_client_id (void)
 {
 	test_config (NULL, quote_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "abcd",
 	             NULL,
 	             "eth0",
@@ -199,6 +193,7 @@ test_quote_client_id_2 (void)
 {
 	test_config (NULL, quote_client_id_expected_2,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "a\\bc",
 	             NULL,
 	             "eth0",
@@ -229,6 +224,7 @@ test_hex_zero_client_id (void)
 {
 	test_config (NULL, hex_zero_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "00:11:22:33",
 	             NULL,
 	             "eth0",
@@ -259,6 +255,7 @@ test_ascii_client_id (void)
 {
 	test_config (NULL, ascii_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "qb:cd:ef:12:34:56",
 	             NULL,
 	             "eth0",
@@ -289,6 +286,7 @@ test_hex_single_client_id (void)
 {
 	test_config (NULL, hex_single_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "ab:cd:e:12:34:56",
 	             NULL,
 	             "eth0",
@@ -327,6 +325,7 @@ test_existing_hex_client_id (void)
 	new_client_id = g_bytes_new (bytes, sizeof (bytes));
 	test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -364,6 +363,7 @@ test_existing_escaped_client_id (void)
 	new_client_id = g_bytes_new ("$test\xfe", 6);
 	test_config (existing_escaped_client_id_orig, existing_escaped_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -405,6 +405,7 @@ test_existing_ascii_client_id (void)
 	new_client_id = g_bytes_new (buf, sizeof (buf));
 	test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -417,7 +418,8 @@ static const char *fqdn_expected = \
 	"\n"
 	"send fqdn.fqdn \"foo.bar.com\"; # added by NetworkManager\n"
 	"send fqdn.encoded on;\n"
-	"send fqdn.server-update on;\n"
+	"send fqdn.server-update off;\n"
+	"send fqdn.no-client-update on;\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -435,7 +437,10 @@ test_fqdn (void)
 {
 	test_config (NULL, fqdn_expected,
 	             AF_INET, "foo.bar.com", 0,
-	             TRUE, NULL,
+	             TRUE,
+	               NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED
+	             | NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE,
+	             NULL,
 	             NULL,
 	             "eth0",
 	             NULL);
@@ -452,8 +457,9 @@ static const char *fqdn_options_override_expected = \
 	"# Merged from /path/to/dhclient.conf\n"
 	"\n"
 	"send fqdn.fqdn \"example2.com\"; # added by NetworkManager\n"
-	"send fqdn.encoded on;\n"
+	"send fqdn.encoded off;\n"
 	"send fqdn.server-update on;\n"
+	"send fqdn.no-client-update off;\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -476,6 +482,7 @@ test_fqdn_options_override (void)
 	test_config (fqdn_options_override_orig,
 	             fqdn_options_override_expected,
 	             AF_INET, "example2.com", 0,
+	             NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE,
 	             TRUE, NULL,
 	             NULL,
 	             "eth0",
@@ -510,6 +517,7 @@ test_override_hostname (void)
 {
 	test_config (override_hostname_orig, override_hostname_expected,
 	             AF_INET, "blahblah", 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -538,6 +546,7 @@ test_override_hostname6 (void)
 {
 	test_config (override_hostname6_orig, override_hostname6_expected,
 	             AF_INET6, "blahblah.local", 0, TRUE,
+	             NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -550,7 +559,7 @@ static const char *nonfqdn_hostname6_expected = \
 	"# Created by NetworkManager\n"
 	"\n"
 	"send fqdn.fqdn \"blahblah\"; # added by NetworkManager\n"
-	"send fqdn.server-update on;\n"
+	"send fqdn.no-client-update on;\n"
 	"\n"
 	"also request dhcp6.name-servers;\n"
 	"also request dhcp6.domain-search;\n"
@@ -563,6 +572,7 @@ test_nonfqdn_hostname6 (void)
 	/* Non-FQDN hostname can now be used with dhclient */
 	test_config (NULL, nonfqdn_hostname6_expected,
 	             AF_INET6, "blahblah", 0, TRUE,
+	             NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -599,6 +609,7 @@ test_existing_alsoreq (void)
 {
 	test_config (existing_alsoreq_orig, existing_alsoreq_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -638,6 +649,7 @@ test_existing_req (void)
 {
 	test_config (existing_req_orig, existing_req_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -678,6 +690,7 @@ test_existing_multiline_alsoreq (void)
 {
 	test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -917,6 +930,7 @@ test_interface1 (void)
 {
 	test_config (interface1_orig, interface1_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -963,6 +977,7 @@ test_interface2 (void)
 {
 	test_config (interface2_orig, interface2_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth1",
@@ -1074,6 +1089,7 @@ test_structured (void)
 	new_client_id = g_bytes_new (bytes, sizeof (bytes) - 1);
 	test_config (orig, expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -1129,6 +1145,7 @@ test_config_req_intf (void)
 
 	test_config (orig, expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c
index e4d4c348..d0389069 100644
--- a/src/dhcp/tests/test-dhcp-utils.c
+++ b/src/dhcp/tests/test-dhcp-utils.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2014 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c
index 13576c92..a5028e42 100644
--- a/src/dns/nm-dns-dnsmasq.c
+++ b/src/dns/nm-dns-dnsmasq.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, 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.
- *
  */
 
 #include "nm-default.h"
@@ -29,6 +15,7 @@
 #include <sys/stat.h>
 #include <linux/if.h>
 
+#include "nm-glib-aux/nm-dbus-aux.h"
 #include "nm-core-internal.h"
 #include "platform/nm-platform.h"
 #include "nm-utils.h"
@@ -37,21 +24,644 @@
 #include "nm-dbus-manager.h"
 #include "NetworkManagerUtils.h"
 
-#define PIDFILE NMRUNDIR "/dnsmasq.pid"
-#define CONFDIR NMCONFDIR "/dnsmasq.d"
+#define PIDFILE    NMRUNDIR "/dnsmasq.pid"
+#define CONFDIR    NMCONFDIR "/dnsmasq.d"
 
 #define DNSMASQ_DBUS_SERVICE "org.freedesktop.NetworkManager.dnsmasq"
-#define DNSMASQ_DBUS_PATH "/uk/org/thekelleys/dnsmasq"
+#define DNSMASQ_DBUS_PATH    "/uk/org/thekelleys/dnsmasq"
+
+#define RATELIMIT_INTERVAL_MSEC    30000
+#define RATELIMIT_BURST            5
+
+#define _NMLOG_DOMAIN      LOGD_DNS
 
 /*****************************************************************************/
 
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "dnsmasq", __VA_ARGS__)
+
+#define WAIT_MSEC_AFTER_SIGTERM 1000
+G_STATIC_ASSERT (WAIT_MSEC_AFTER_SIGTERM <= NM_SHUTDOWN_TIMEOUT_MS);
+
+#define WAIT_MSEC_AFTER_SIGKILL 400
+G_STATIC_ASSERT (WAIT_MSEC_AFTER_SIGKILL + 100 <= NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG);
+
+typedef void (*GlPidSpawnAsyncNotify) (GCancellable *cancellable,
+                                       GPid pid,
+                                       const int *p_exit_code,
+                                       GError *error,
+                                       gpointer notify_user_data);
+
 typedef struct {
-	GDBusProxy *dnsmasq;
-	GCancellable *dnsmasq_cancellable;
-	GCancellable *update_cancellable;
-	gboolean running;
+	NMShutdownWaitObjHandle *shutdown_wait_handle;
+	guint64 p_start_time;
+	gint64 started_at;
+	GPid pid;
+	bool sigkilled:1;
+} GlPidKillExternalData;
+
+typedef struct {
+	const char *dm_binary;
+	GlPidSpawnAsyncNotify notify;
+	gpointer notify_user_data;
+	GCancellable *cancellable;
+} GlPidSpawnAsyncData;
+
+static struct {
+
+	GlPidKillExternalData *kill_external_data;
+
+	GlPidSpawnAsyncData *spawn_data;
+
+	NMShutdownWaitObjHandle *terminate_handle;
+
+	GPid pid;
+
+	guint terminate_timeout_id;
+
+	guint watch_id;
+
+	/* whether the external process (with the pid from PIDFILE) was already killed.
+	 * This only happens once, once we do that, we remember to not do it again.
+	 * The reason is that later one, when we want to kill the process it's a
+	 * child process. So, we wait for the exit code. */
+	bool kill_external_done:1;
+
+	bool terminate_sigkill:1;
+} gl_pid;
+
+/*****************************************************************************/
+
+static void _gl_pid_spawn_next_step (void);
+static void _gl_pid_spawn_cancelled_cb (GCancellable *cancellable,
+                                        GlPidSpawnAsyncData *sdata);
+
+/*****************************************************************************/
+
+static gboolean
+_gl_pid_unlink_pidfile (gboolean do_unlink)
+{
+	int errsv;
+
+	if (do_unlink) {
+		if (unlink (PIDFILE) == 0)
+			_LOGD ("spawn: delete PID file %s", PIDFILE);
+		else {
+			errsv = errno;
+			if (errsv != ENOENT)
+				_LOGD ("spawn: delete PID file %s failed: %s (%d)", PIDFILE, nm_strerror_native (errsv), errsv);
+		}
+	}
+	return TRUE;
+}
+
+static gboolean
+_gl_pid_kill_external_timeout_cb (gpointer user_data)
+{
+	guint64 p_start_time;
+	char p_state = '\0';
+	gint64 now;
+
+	p_start_time = nm_utils_get_start_time_for_pid (gl_pid.kill_external_data->pid, &p_state, NULL);
+	if (   p_start_time == 0
+	    || p_start_time != gl_pid.kill_external_data->p_start_time
+	    || nm_utils_process_state_is_dead (p_state)) {
+		_LOGD ("spawn: process %"G_PID_FORMAT" from pidfile %s is gone", gl_pid.kill_external_data->pid, PIDFILE);
+		goto process_gone;
+	}
+
+	now = nm_utils_get_monotonic_timestamp_ms ();
+
+	if (gl_pid.kill_external_data->started_at + WAIT_MSEC_AFTER_SIGTERM < now) {
+		if (!gl_pid.kill_external_data->sigkilled) {
+			_LOGD ("spawn: send SIGKILL to process %"G_PID_FORMAT" from pidfile %s", gl_pid.kill_external_data->pid, PIDFILE);
+			gl_pid.kill_external_data->sigkilled = TRUE;
+			kill (gl_pid.kill_external_data->pid, SIGKILL);
+		} else if (gl_pid.kill_external_data->started_at + WAIT_MSEC_AFTER_SIGTERM + WAIT_MSEC_AFTER_SIGKILL < now) {
+			_LOGW ("spawn: process %"G_PID_FORMAT" from pidfile %s is still here after trying to kill it. Wait no longer", gl_pid.kill_external_data->pid, PIDFILE);
+			goto process_gone;
+		}
+	}
+
+	return G_SOURCE_CONTINUE;
+
+process_gone:
+	nm_shutdown_wait_obj_unregister (gl_pid.kill_external_data->shutdown_wait_handle);
+	g_slice_free (GlPidKillExternalData, g_steal_pointer (&gl_pid.kill_external_data));
+
+	_gl_pid_unlink_pidfile (TRUE);
+
+	_gl_pid_spawn_next_step ();
+
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_gl_pid_kill_external (void)
+{
+	gs_free char *contents = NULL;
+	gs_free char *cmdline_contents = NULL;
+	gs_free_error GError *error = NULL;
+	gint64 pid64;
+	GPid pid = 0;
+	guint64 p_start_time = 0;
+	char proc_path[256];
+	gboolean do_kill = FALSE;
+	char p_state = '\0';
+	gboolean do_unlink = TRUE;
+	int errsv;
+
+	if (gl_pid.kill_external_done) {
+		if (gl_pid.kill_external_data) {
+			_LOGD ("spawn: waiting for external process %"G_PID_FORMAT" from pidfile %s quit", gl_pid.kill_external_data->pid, PIDFILE);
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	if (!g_file_get_contents (PIDFILE, &contents, NULL, &error)) {
+		if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			do_unlink = FALSE;
+		_LOGD ("spawn: failure to read pidfile %s: %s", PIDFILE, error->message);
+		g_clear_error (&error);
+		goto handle_kill;
+	}
+
+	pid64 = _nm_utils_ascii_str_to_int64 (contents, 10, 2, G_MAXINT64, -1);
+	if (   pid64 == -1
+	    || (pid = (GPid) pid64) != pid64) {
+		_LOGD ("spawn: pidfile %s does not contain a valid process identifier", PIDFILE);
+		goto handle_kill;
+	}
+
+	G_STATIC_ASSERT_EXPR (sizeof (pid) == sizeof (pid_t));
+
+	p_start_time = nm_utils_get_start_time_for_pid (pid, &p_state, NULL);
+	if (p_start_time == 0) {
+		_LOGD ("spawn: process %"G_PID_FORMAT" from pidfile %s seems to no longer exist", pid, PIDFILE);
+		goto handle_kill;
+	}
+
+	nm_sprintf_buf (proc_path, "/proc/%"G_PID_FORMAT"/cmdline", pid);
+	if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL)) {
+		_LOGD ("spawn: process %"G_PID_FORMAT" from pidfile %s seems to no longer exist", pid, PIDFILE);
+		goto handle_kill;
+	}
+
+	if (!strstr (cmdline_contents, "/dnsmasq")) {
+		_LOGD ("spawn: process %"G_PID_FORMAT" from pidfile %s seems to no longer to be a dnsmasq process", pid, PIDFILE);
+		goto handle_kill;
+	}
+
+	do_kill = TRUE;
+
+handle_kill:
+
+	gl_pid.kill_external_done = TRUE;
+
+	if (!do_kill)
+		return _gl_pid_unlink_pidfile (do_unlink);
+
+	if (nm_utils_process_state_is_dead (p_state)) {
+		_LOGD ("spawn: process %"G_PID_FORMAT" from pidfile %s is already a zombie", pid, PIDFILE);
+		return _gl_pid_unlink_pidfile (do_unlink);
+	}
+
+	if (kill (pid, SIGTERM) != 0) {
+		errsv = errno;
+		if (errsv == ESRCH)
+			_LOGD ("spawn: process %"G_PID_FORMAT" from pidfile %s no longer exists", pid, PIDFILE);
+		else
+			_LOGD ("spawn: process %"G_PID_FORMAT" from pidfile %s failed with \"%s\" (%d)", pid, PIDFILE, nm_strerror_native (errsv), errsv);
+		return _gl_pid_unlink_pidfile (do_unlink);
+	}
+
+	_LOGD ("spawn: waiting for process %"G_PID_FORMAT" from pidfile %s to terminate after SIGTERM", pid, PIDFILE);
+
+	gl_pid.kill_external_data = g_slice_new (GlPidKillExternalData);
+	*gl_pid.kill_external_data = (GlPidKillExternalData) {
+		.shutdown_wait_handle = nm_shutdown_wait_obj_register_handle_full (g_strdup_printf ("kill-external-dnsmasq-process-%"G_PID_FORMAT, pid), TRUE),
+		.started_at           = nm_utils_get_monotonic_timestamp_ms (),
+		.pid                  = pid,
+		.p_start_time         = p_start_time,
+	};
+	g_timeout_add (50, _gl_pid_kill_external_timeout_cb, NULL);
+	return FALSE;
+}
+
+/*****************************************************************************/
+
+static gboolean
+_gl_pid_spawn_clear_pid (void)
+{
+	gboolean was_stopping = !!gl_pid.terminate_handle;
+
+	gl_pid.pid = 0;
+	gl_pid.terminate_sigkill = FALSE;
+	nm_clear_g_source (&gl_pid.watch_id);
+	nm_clear_g_source (&gl_pid.terminate_timeout_id);
+	nm_clear_pointer (&gl_pid.terminate_handle, nm_shutdown_wait_obj_unregister);
+	return was_stopping;
+}
+
+static void
+_gl_pid_spawn_register_for_termination (void)
+{
+	if (   gl_pid.pid > 0
+	    && !gl_pid.terminate_handle) {
+		/* Create a shtudown handle as a reminder that the currently running process must be terminated
+		 * first. This also happens to block shutdown... */
+		gl_pid.terminate_handle = nm_shutdown_wait_obj_register_handle_full (g_strdup_printf ("kill-dnsmasq-process-%"G_PID_FORMAT, gl_pid.pid), TRUE);
+	}
+}
+
+/**
+ * _gl_pid_spawn_notify:
+ * @sdata: the notify data. @sdata might be destroyed by the function,
+ *   depending on the other arguments (which indicate whether the
+ *   task is complete).
+ * @pid: the PID to notify (argument for GlPidSpawnAsyncNotify)
+ * @p_exit_code: the exit code to notify (argument for GlPidSpawnAsyncNotify)
+ * @error: error reason to notify (argument for GlPidSpawnAsyncNotify)
+ *
+ * The GlPidSpawnAsyncNotify callback passed to _gl_pid_spawn() is used
+ * for two purposes:
+ *
+ *  - signal that the dnsmasq process was spawned (or failed to be spawned).
+ *  - signal that the dnsmasq process quit (if it was spawned sucessfully before).
+ *
+ * Depending on the arguments, the callee can see what's the case.
+ */
+static void
+_gl_pid_spawn_notify (GlPidSpawnAsyncData *sdata,
+                      GPid pid,
+                      const int *p_exit_code,
+                      GError *error)
+{
+	gboolean destroy = TRUE;
+
+	nm_assert (sdata);
+
+	if (error) {
+		nm_assert (pid == 0);
+		nm_assert (!p_exit_code);
+		if (!nm_utils_error_is_cancelled (error, FALSE))
+			_LOGD ("spawn: dnsmasq failed: %s", error->message);
+	} else if (p_exit_code) {
+		/* the only caller already logged about this condition extensively. */
+		nm_assert (pid > 0);
+	} else {
+		nm_assert (pid > 0);
+		_LOGD ("spawn: dnsmasq started with pid %"G_PID_FORMAT, pid);
+		destroy = FALSE;
+	}
+
+	nm_assert ((!!destroy) == (sdata != gl_pid.spawn_data));
+
+	if (destroy)
+		g_signal_handlers_disconnect_by_func (sdata->cancellable, _gl_pid_spawn_cancelled_cb, sdata);
+
+	sdata->notify (sdata->cancellable,
+	               pid,
+	               p_exit_code,
+	               error,
+	               sdata->notify_user_data);
+
+	if (destroy) {
+		g_clear_object (&sdata->cancellable);
+		nm_g_slice_free (sdata);
+	}
+}
+
+static void
+_gl_pid_spawn_cancelled_cb (GCancellable *cancellable,
+                            GlPidSpawnAsyncData *sdata)
+{
+	gs_free_error GError *error = NULL;
+
+	if (sdata == gl_pid.spawn_data) {
+		gl_pid.spawn_data = NULL;
+
+		/* When the cancellable gets cancelled, we terminate the current dnsmasq instance
+		 * in the background. The only way for keeping dnsmasq running while unregistering
+		 * the callback is by calling _gl_pid_spawn() without a new callback. */
+		_gl_pid_spawn_register_for_termination ();
+	} else
+		nm_assert_not_reached ();
+
+	if (!g_cancellable_set_error_if_cancelled (cancellable, &error))
+		nm_assert_not_reached ();
+
+	_gl_pid_spawn_notify (sdata, 0, NULL, error);
+
+	_gl_pid_spawn_next_step ();
+}
+
+static gboolean
+_gl_pid_spawn_terminate_timeout_cb (gpointer user_data)
+{
+	nm_assert (gl_pid.terminate_timeout_id != 0);
+	nm_assert (gl_pid.pid > 0);
+	nm_assert (gl_pid.terminate_handle);
+	nm_assert (gl_pid.watch_id != 0);
+
+	gl_pid.terminate_timeout_id = 0;
+
+	if (!gl_pid.terminate_sigkill) {
+		gl_pid.terminate_sigkill = TRUE;
+		_LOGD ("spawn: send SIGKILL signal to dnsmasq process %"G_PID_FORMAT" as it did not exit yet", gl_pid.pid);
+		kill (gl_pid.pid, SIGKILL);
+		gl_pid.terminate_timeout_id = g_timeout_add (WAIT_MSEC_AFTER_SIGKILL, _gl_pid_spawn_terminate_timeout_cb, NULL);
+	} else {
+		_LOGE ("spawn: process %"G_PID_FORMAT" did not exit even after SIGTERM and SIGKILL", gl_pid.pid);
+
+		/* we don't unregister the watch. Just forget about it. We still want to reap the child eventually. */
+		gl_pid.watch_id = 0;
+
+		_gl_pid_spawn_clear_pid ();
+		_gl_pid_spawn_next_step ();
+	}
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+_gl_pid_spawn_watch_cb (GPid pid,
+                        int status,
+                        gpointer user_data)
+{
+	int err;
+	gboolean was_stopping;
+
+	nm_assert (pid > 0);
+
+	if (WIFEXITED (status)) {
+		err = WEXITSTATUS (status);
+		if (err) {
+			char sbuf[100];
+
+			_LOGW ("spawn: dnsmasq process %"G_PID_FORMAT" exited with error: %s",
+			       pid, nm_utils_dnsmasq_status_to_string (err, sbuf, sizeof (sbuf)));
+		} else
+			_LOGD ("spawn: dnsmasq process %"G_PID_FORMAT" exited normally", pid);
+	} else if (WIFSTOPPED (status))
+		_LOGW ("spawn: dnsmasq process %"G_PID_FORMAT" stopped unexpectedly with signal %d", pid, WSTOPSIG (status));
+	else if (WIFSIGNALED (status))
+		_LOGW ("spawn: dnsmasq process %"G_PID_FORMAT" died with signal %d", pid, WTERMSIG (status));
+	else
+		_LOGW ("spawn: dnsmasq process %"G_PID_FORMAT" died from an unknown cause (status %d)", pid, status);
+
+	if (gl_pid.pid != pid) {
+		/* this can only happen, if we timed out and no longer care about this PID.
+		 * We still kept the watch-id active, to reap the process. Nothing to do. */
+		return;
+	}
+
+	nm_assert (gl_pid.watch_id != 0);
+
+	gl_pid.watch_id = 0;
+
+	_gl_pid_unlink_pidfile (TRUE);
+
+	was_stopping = _gl_pid_spawn_clear_pid ();
+
+	if (gl_pid.spawn_data) {
+		if (was_stopping) {
+			/* The current process was scheduled to be terminated. That means the pending
+			 * spawn_data is not for that former instance, but for starting a new one.
+			 * This spawn-request is not yet complete, instead it's just about to start. */
+		} else
+			_gl_pid_spawn_notify (g_steal_pointer (&gl_pid.spawn_data), pid, &status, NULL);
+	}
+
+	_gl_pid_spawn_next_step ();
+}
+
+/**
+ * _gl_pid_spawn_next_step:
+ *
+ * The state about a running dnsmasq process is tracked in @gl_pid. There are
+ * various things that can happen
+ *
+ *   - user calls _gl_pid_spawn() -- which might terminate an existing run first.
+ *   - user might cancel the GCancellable -- which would abort the spawning or
+ *     kill the current instance.
+ *   - the child process might exit.
+ *
+ * In all these cases, we call _gl_pid_spawn_next_step() to check what to do next.
+ */
+static void
+_gl_pid_spawn_next_step (void)
+{
+	gs_free_error GError *error = NULL;
+	const char *argv[15];
+	GPid pid = 0;
+	guint argv_idx;
+
+	if (!_gl_pid_kill_external ()) {
+		/* we need to wait to kill the instance from the PID file first. */
+		return;
+	}
+
+	if (gl_pid.terminate_handle) {
+
+		nm_assert (gl_pid.pid > 0);
+
+		if (gl_pid.terminate_timeout_id == 0) {
+			_LOGD ("spawn: send SIGTERM signal to process %"G_PID_FORMAT, gl_pid.pid);
+			gl_pid.terminate_timeout_id = g_timeout_add (WAIT_MSEC_AFTER_SIGTERM, _gl_pid_spawn_terminate_timeout_cb, NULL);
+			kill (gl_pid.pid, SIGTERM);
+		}
+
+		/* we can only wait for the process to exit. */
+		return;
+	}
+
+	if (!gl_pid.spawn_data) {
+		/* we are not requested to spawn another process. */
+		nm_assert (gl_pid.pid == 0);
+		return;
+	}
+
+	if (gl_pid.pid > 0) {
+		/* the process we desire is already running. All good. */
+		return;
+	}
+
+	argv_idx = 0;
+	argv[argv_idx++] = gl_pid.spawn_data->dm_binary;
+	argv[argv_idx++] = "--no-resolv";  /* Use only commandline */
+	argv[argv_idx++] = "--keep-in-foreground";
+	argv[argv_idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */
+	argv[argv_idx++] = "--bind-interfaces";
+	argv[argv_idx++] = "--pid-file=" PIDFILE;
+	argv[argv_idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
+	argv[argv_idx++] = "--cache-size=400";
+	argv[argv_idx++] = "--clear-on-reload"; /* clear cache when dns server changes */
+	argv[argv_idx++] = "--conf-file=/dev/null"; /* avoid loading /etc/dnsmasq.conf */
+	argv[argv_idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */
+	argv[argv_idx++] = "--enable-dbus=" DNSMASQ_DBUS_SERVICE;
+
+	/* dnsmasq exits if the conf dir is not present */
+	if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR))
+		argv[argv_idx++] = "--conf-dir=" CONFDIR;
+
+	argv[argv_idx++] = NULL;
+	nm_assert (argv_idx <= G_N_ELEMENTS (argv));
+
+	if (!_LOGD_ENABLED ())
+		_LOGI ("starting %s", gl_pid.spawn_data->dm_binary);
+	else {
+		gs_free char *cmdline = NULL;
+
+		_LOGD ("spawn: starting dnsmasq: %s",
+		      (cmdline = g_strjoinv (" ", (char **) argv)));
+	}
+
+	if (!g_spawn_async (NULL,
+	                    (char **) argv,
+	                    NULL,
+	                    G_SPAWN_DO_NOT_REAP_CHILD,
+	                    nm_utils_setpgid,
+	                    NULL,
+	                    &pid,
+	                    &error)) {
+		_gl_pid_spawn_notify (g_steal_pointer (&gl_pid.spawn_data), 0, NULL, error);
+		return;
+	}
+
+	gl_pid.pid = pid;
+	gl_pid.watch_id = g_child_watch_add (pid, _gl_pid_spawn_watch_cb, NULL);
+
+	_gl_pid_spawn_notify (gl_pid.spawn_data, pid, NULL, NULL);
+}
+
+/**
+ * _gl_pid_spawn:
+ * @dm_binary: the binary name for dnsmasq to spawn. We could
+ *   detect it ad-hoc right when needing it. But that would be
+ *   asynchronously, and if dnsmasq is not in $PATH, we want to
+ *   fail right away (synchrounously). Hence, @dm_binary is
+ *   an argument.
+ * @cancellable: abort the operation. This will invoke the callback
+ *   a last time. Also, if the dnsmasq process is currently running,
+ *   it will be terminated in the background. To unregister a notify
+ *   call without killing the dnsmasq process, call _gl_pid_spawn()
+ *   again with all arguments %NULL.
+ * @notify: the callback when the process is started successfully
+ *   and when the process terminates.
+ * @notify_user_data: user-data for callback.
+ *
+ * If a dnsmasq process is already running (from a previous call of
+ * _gl_pid_spawn()), that one will be replaced. Meaning, the other notify
+ * callback will be invoked with NM_UTILS_ERROR/NM_UTILS_ERROR_CANCELLED_DISPOSING.
+ * If you the @dm_binary argument, the previously running process will
+ * also be terminated first, before spawning a new instance.
+ * However, you may also pass all arguments as %NULL. In that case, the
+ * previous @notify will be completed (and forgotten), but the dnsmasq
+ * process will be left running in the background.
+ *
+ * So, you can:
+ *
+ *   - call _gl_pid_spawn() with a @dm_binary argument. The previous
+ *     notify() completes with NM_UTILS_ERROR_CANCELLED_DISPOSING and
+ *     the dnsmasq process gets killed.
+ *   - cancel the GCancellable, in this case the notify() completes
+ *     with G_IO_ERROR_CANCELLED and the dnsmasq process gets killed.
+ *   - call _gl_pid_spawn() with all arguments %NULL. In that case
+ *     the previous notify() completes with NM_UTILS_ERROR_CANCELLED_DISPOSING
+ *     but the dnsmasq process keeps running in the background.
+ *
+ * The callback is used in two cases.
+ * - When spawning the process it will be invoked always exactly once.
+ *   In this case the callback might be invoked synchronously or
+ *   asynchronously.
+ *   This either provides a PID or a failure reason. In case of a
+ *   failure, that's the end and the process is not running.
+ * - if the process could be spawned, the child process with the
+ *   provided PID gets monitored. When the process exits, the callback
+ *   will be invoked again, with a failure reason. This is always done
+ *   asynchronously.
+ */
+static void
+_gl_pid_spawn (const char *dm_binary,
+               GCancellable *cancellable,
+               GlPidSpawnAsyncNotify notify,
+               gpointer notify_user_data)
+{
+	GlPidSpawnAsyncData *sdata_replace;
+
+	sdata_replace = g_steal_pointer (&gl_pid.spawn_data);
+
+	if (dm_binary) {
+		nm_assert (notify);
+		nm_assert (G_IS_CANCELLABLE (cancellable));
+		gl_pid.spawn_data = g_slice_new (GlPidSpawnAsyncData);
+		*gl_pid.spawn_data = (GlPidSpawnAsyncData) {
+			.dm_binary        = dm_binary,
+			.notify           = notify,
+			.notify_user_data = notify_user_data,
+			.cancellable      = g_object_ref (cancellable),
+		};
+		g_signal_connect (cancellable, "cancelled", G_CALLBACK (_gl_pid_spawn_cancelled_cb), gl_pid.spawn_data);
+
+		/* If dnsmasq is running, we terminate it and start a new instance.
+		 *
+		 * If the user would not provide a new callback, this would mean to fail/abort
+		 * the currently subscribed notification (below). But it would leave the dnsmasq
+		 * instance running in the background.
+		 * This allows the user to say to not care about the current instance
+		 * anymore, but still leave it running.
+		 *
+		 * To kill the dnsmasq process without scheduling a new one, cancel the cancellable
+		 * instead. */
+		_gl_pid_spawn_register_for_termination ();
+	} else {
+		nm_assert (!notify);
+		nm_assert (!cancellable);
+		nm_assert (!notify_user_data);
+	}
+
+	if (sdata_replace) {
+		gs_free_error GError *error = NULL;
+
+		/* we don't mark the error as G_IO_ERROR/G_IO_ERROR_CANCELLED. That
+		 * is reserved for cancelling the cancellable. However, the current
+		 * request was obsoleted/replaced by a new one, so we fail it with
+		 * NM_UTILS_ERROR/NM_UTILS_ERROR_CANCELLED_DISPOSING. */
+		nm_utils_error_set_cancelled (&error, TRUE, NULL);
+		_gl_pid_spawn_notify (sdata_replace, 0, NULL, error);
+	}
+
+	_gl_pid_spawn_next_step ();
+}
+
+/*****************************************************************************/
+
+typedef struct {
+
+	GDBusConnection *dbus_connection;
 
 	GVariant *set_server_ex_args;
+
+	GCancellable *update_cancellable;
+
+	GCancellable *main_cancellable;
+
+	char *name_owner;
+
+	gint64 burst_start_at;
+
+	GPid process_pid;
+
+	guint name_owner_changed_id;
+	guint main_timeout_id;
+
+	guint burst_retry_timeout_id;
+
+	guint8 burst_count;
+
+	bool is_stopped:1;
+
 } NMDnsDnsmasqPrivate;
 
 struct _NMDnsDnsmasq {
@@ -69,11 +679,15 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
 
 /*****************************************************************************/
 
-#define _NMLOG_DOMAIN         LOGD_DNS
+#undef _NMLOG
 #define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG_DOMAIN, "dnsmasq", __VA_ARGS__)
 
 /*****************************************************************************/
 
+static gboolean start_dnsmasq (NMDnsDnsmasq *self, gboolean force_start, GError **error);
+
+/*****************************************************************************/
+
 static void
 add_dnsmasq_nameserver (NMDnsDnsmasq *self,
                         GVariantBuilder *servers,
@@ -192,19 +806,42 @@ add_ip_config (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfig
 	}
 }
 
+static GVariant *
+create_update_args (NMDnsDnsmasq *self,
+                    const NMGlobalDnsConfig *global_config,
+                    const CList *ip_config_lst_head,
+                    const char *hostname)
+{
+	GVariantBuilder servers;
+	const NMDnsIPConfigData *ip_data;
+
+	g_variant_builder_init (&servers, G_VARIANT_TYPE ("aas"));
+
+	if (global_config)
+		add_global_config (self, &servers, global_config);
+	else {
+		c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst)
+			add_ip_config (self, &servers, ip_data);
+	}
+
+	return g_variant_new ("(aas)", &servers);
+}
+
+/*****************************************************************************/
+
 static void
-dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
+dnsmasq_update_done (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
 	NMDnsDnsmasq *self;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *response = NULL;
 
-	response = g_dbus_proxy_call_finish (proxy, res, &error);
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
+	response = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
 
-	self = NM_DNS_DNSMASQ (user_data);
+	if (nm_utils_error_is_cancelled (error, FALSE))
+		return;
 
+	self = user_data;
 	if (!response)
 		_LOGW ("dnsmasq update failed: %s", error->message);
 	else
@@ -216,238 +853,287 @@ send_dnsmasq_update (NMDnsDnsmasq *self)
 {
 	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
 
-	if (!priv->set_server_ex_args)
-		return;
+	if (   !priv->name_owner
+	    || !priv->set_server_ex_args)
+	    return;
 
-	if (priv->running) {
-		_LOGD ("trying to update dnsmasq nameservers");
-
-		nm_clear_g_cancellable (&priv->update_cancellable);
-		priv->update_cancellable = g_cancellable_new ();
-
-		g_dbus_proxy_call (priv->dnsmasq,
-		                   "SetServersEx",
-		                   priv->set_server_ex_args,
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   priv->update_cancellable,
-		                   (GAsyncReadyCallback) dnsmasq_update_done,
-		                   self);
-		g_clear_pointer (&priv->set_server_ex_args, g_variant_unref);
-	} else
-		_LOGD ("dnsmasq not found on the bus. The nameserver update will be sent when dnsmasq appears");
+	_LOGD ("trying to update dnsmasq nameservers");
+
+	nm_clear_g_cancellable (&priv->update_cancellable);
+	priv->update_cancellable = g_cancellable_new ();
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        priv->name_owner,
+	                        DNSMASQ_DBUS_PATH,
+	                        DNSMASQ_DBUS_SERVICE,
+	                        "SetServersEx",
+	                        priv->set_server_ex_args,
+	                        NULL,
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        20000,
+	                        priv->update_cancellable,
+	                        dnsmasq_update_done,
+	                        self);
 }
 
+/*****************************************************************************/
+
 static void
-name_owner_changed (GObject    *object,
-                    GParamSpec *pspec,
-                    gpointer    user_data)
+_main_cleanup (NMDnsDnsmasq *self, gboolean emit_failed)
 {
-	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (user_data);
 	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
-	gs_free char *owner = NULL;
 
-	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
-	if (owner) {
-		_LOGI ("dnsmasq appeared as %s", owner);
-		priv->running = TRUE;
+	if (!priv->main_cancellable)
+		return;
+
+	priv->process_pid = 0;
+	nm_clear_g_free (&priv->name_owner);
+
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->name_owner_changed_id);
+
+	nm_clear_g_source (&priv->main_timeout_id);
+	nm_clear_g_cancellable (&priv->update_cancellable);
+
+	/* cancelling the main_cancellable will also cause _gl_pid_spawn*() to terminate the
+	 * process in the background. */
+	nm_clear_g_cancellable (&priv->main_cancellable);
+
+	if (   !priv->is_stopped
+	    && priv->burst_retry_timeout_id == 0) {
+		start_dnsmasq (self, FALSE, NULL);
 		send_dnsmasq_update (self);
-	} else {
-		if (priv->running) {
-			_LOGI ("dnsmasq disappeared");
-			priv->running = FALSE;
-			g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
-		} else {
-			/* The only reason for which (!priv->running) here
-			 * is that the dnsmasq process quit. We don't care
-			 * of that here, the manager handles child restarts
-			 * by itself. */
-		}
 	}
 }
 
 static void
-dnsmasq_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+name_owner_changed (NMDnsDnsmasq *self,
+                    const char *name_owner)
 {
-	NMDnsDnsmasq *self;
-	NMDnsDnsmasqPrivate *priv;
-	gs_free_error GError *error = NULL;
-	gs_free char *owner = NULL;
-	GDBusProxy *proxy;
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
 
-	proxy = g_dbus_proxy_new_finish (res, &error);
-	if (   !proxy
-	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+	name_owner = nm_str_not_empty (name_owner);
+
+	if (nm_streq0 (priv->name_owner, name_owner))
 		return;
 
-	self = NM_DNS_DNSMASQ (user_data);
+	g_free (priv->name_owner);
+	priv->name_owner = g_strdup (name_owner);
 
-	if (!proxy) {
-		_LOGW ("failed to connect to dnsmasq via DBus: %s", error->message);
-		g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
+	if (!name_owner) {
+		_LOGT ("D-Bus name for dnsmasq disappeared");
+		_main_cleanup (self, TRUE);
 		return;
 	}
 
-	priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	_LOGT ("D-Bus name for dnsmasq got owner %s", name_owner);
+	nm_clear_g_source (&priv->main_timeout_id);
+	send_dnsmasq_update (self);
+}
+
+static void
+name_owner_changed_cb (GDBusConnection *connection,
+                       const char *sender_name,
+                       const char *object_path,
+                       const char *interface_name,
+                       const char *signal_name,
+                       GVariant *parameters,
+                       gpointer user_data)
+{
+	NMDnsDnsmasq *self = user_data;
+	const char *new_owner;
 
-	priv->dnsmasq = proxy;
-	nm_clear_g_cancellable (&priv->dnsmasq_cancellable);
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
+		return;
 
-	_LOGD ("dnsmasq proxy creation successful");
+	g_variant_get (parameters,
+	               "(&s&s&s)",
+	               NULL,
+	               NULL,
+	               &new_owner);
 
-	g_signal_connect (priv->dnsmasq, "notify::g-name-owner",
-	                  G_CALLBACK (name_owner_changed), self);
-	owner = g_dbus_proxy_get_name_owner (priv->dnsmasq);
-	priv->running = (owner != NULL);
+	name_owner_changed (self, new_owner);
+}
 
-	if (priv->running)
-		send_dnsmasq_update (self);
+static void
+get_name_owner_cb (const char *name_owner,
+                   GError *error,
+                   gpointer user_data)
+{
+	if (   !name_owner
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	name_owner_changed (user_data, name_owner);
+}
+
+static gboolean
+spawn_timeout_cb (gpointer user_data)
+{
+	NMDnsDnsmasq *self = user_data;
+
+	_LOGW ("timeout waiting for dnsmasq to appear on D-Bus");
+	_main_cleanup (self, TRUE);
+	return G_SOURCE_REMOVE;
 }
 
 static void
-start_dnsmasq (NMDnsDnsmasq *self)
+spawn_notify (GCancellable *cancellable,
+              GPid pid,
+              const int *p_exit_code,
+              GError *error,
+              gpointer notify_user_data)
 {
-	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
-	const char *dm_binary;
-	const char *argv[15];
-	GPid pid = 0;
-	guint idx = 0;
+	NMDnsDnsmasq *self;
+	NMDnsDnsmasqPrivate *priv;
 
-	if (priv->running) {
-		/* the dnsmasq process is running. Nothing to do. */
+	if (nm_utils_error_is_cancelled (error, FALSE))
 		return;
-	}
 
-	if (nm_dns_plugin_child_pid ((NMDnsPlugin *) self) > 0) {
-		/* if we already have a child process spawned, don't do
-		 * it again. */
+	self = notify_user_data;
+	priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	if (   error
+	    || p_exit_code) {
+		_main_cleanup (self, TRUE);
 		return;
 	}
 
+	nm_assert (pid > 0);
+	priv->process_pid = pid;
+
+	priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+	                                                                                      DNSMASQ_DBUS_SERVICE,
+	                                                                                      name_owner_changed_cb,
+	                                                                                      self,
+	                                                                                      NULL);
+	nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+	                                        DNSMASQ_DBUS_SERVICE,
+	                                        -1,
+	                                        priv->main_cancellable,
+	                                        get_name_owner_cb,
+	                                        self);
+}
+
+static gboolean
+_burst_retry_timeout_cb (gpointer user_data)
+{
+	NMDnsDnsmasq *self = user_data;
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+
+	priv->burst_retry_timeout_id = 0;
+
+	start_dnsmasq (self, TRUE, NULL);
+	send_dnsmasq_update (self);
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+start_dnsmasq (NMDnsDnsmasq *self, gboolean force_start, GError **error)
+{
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	const char *dm_binary;
+	gint64 now;
+
+	if (G_LIKELY (priv->main_cancellable)) {
+		/* The process is already running or about to be started. Nothing to do. */
+		return TRUE;
+	}
+
 	dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL);
 	if (!dm_binary) {
-		_LOGW ("could not find dnsmasq binary");
-		return;
+		/* We resolve the binary name before trying to start it asynchronously.
+		 * The reason is, that if dnsmasq is not installed, we want to fail early,
+		 * so that NMDnsManager can fallback to a non-caching implementation. */
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    "could not find dnsmasq binary");
+		return FALSE;
 	}
 
-	argv[idx++] = dm_binary;
-	argv[idx++] = "--no-resolv";  /* Use only commandline */
-	argv[idx++] = "--keep-in-foreground";
-	argv[idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */
-	argv[idx++] = "--bind-interfaces";
-	argv[idx++] = "--pid-file=" PIDFILE;
-	argv[idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
-	argv[idx++] = "--cache-size=400";
-	argv[idx++] = "--clear-on-reload"; /* clear cache when dns server changes */
-	argv[idx++] = "--conf-file=/dev/null"; /* avoid loading /etc/dnsmasq.conf */
-	argv[idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */
-	argv[idx++] = "--enable-dbus=" DNSMASQ_DBUS_SERVICE;
-
-	/* dnsmasq exits if the conf dir is not present */
-	if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR))
-		argv[idx++] = "--conf-dir=" CONFDIR;
+	if (!priv->dbus_connection) {
+		priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
+		if (!priv->dbus_connection) {
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    "no D-Bus connection available to talk to dnsmasq");
+			return FALSE;
+		}
+	}
 
-	argv[idx++] = NULL;
-	nm_assert (idx <= G_N_ELEMENTS (argv));
+	now = nm_utils_get_monotonic_timestamp_ms ();
+	if (   force_start
+	    || priv->burst_start_at == 0
+	    || priv->burst_start_at + RATELIMIT_INTERVAL_MSEC <= now) {
+		priv->burst_start_at = now;
+		priv->burst_count = 1;
+		nm_clear_g_source (&priv->burst_retry_timeout_id);
+		_LOGT ("rate-limit: start burst interval of %d seconds %s",
+		       RATELIMIT_INTERVAL_MSEC / 1000,
+		       force_start ? " (force)" : "");
+	} else if (priv->burst_count < RATELIMIT_BURST) {
+		nm_assert (priv->burst_retry_timeout_id == 0);
+		priv->burst_count++;
+		_LOGT ("rate-limit: %u try within burst interval of %d seconds",
+		       (guint) priv->burst_count,
+		       RATELIMIT_INTERVAL_MSEC / 1000);
+	} else {
+		if (priv->burst_retry_timeout_id == 0) {
+			_LOGW ("dnsmasq dies and gets respawned too quickly. Back off. Something is very wrong");
+			priv->burst_retry_timeout_id = g_timeout_add_seconds ((2 * RATELIMIT_INTERVAL_MSEC) / 1000, _burst_retry_timeout_cb, self);
+		} else
+			_LOGT ("rate-limit: currently rate-limited from restart");
+		return TRUE;
+	}
 
-	/* And finally spawn dnsmasq */
-	pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");
-	if (!pid)
-		return;
+	priv->main_timeout_id = g_timeout_add (10000,
+	                                       spawn_timeout_cb,
+	                                       self);
 
-	if (   priv->dnsmasq
-	    || priv->dnsmasq_cancellable) {
-		/* we already have a proxy or are about to create it.
-		 * We are done. */
-		return;
-	}
+	priv->main_cancellable = g_cancellable_new ();
 
-	priv->dnsmasq_cancellable = g_cancellable_new ();
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
-	                          NULL,
-	                          DNSMASQ_DBUS_SERVICE,
-	                          DNSMASQ_DBUS_PATH,
-	                          DNSMASQ_DBUS_SERVICE,
-	                          priv->dnsmasq_cancellable,
-	                          dnsmasq_proxy_cb,
-	                          self);
+	_gl_pid_spawn (dm_binary,
+	               priv->main_cancellable,
+	               spawn_notify,
+	               self);
+	return TRUE;
 }
 
 static gboolean
 update (NMDnsPlugin *plugin,
         const NMGlobalDnsConfig *global_config,
         const CList *ip_config_lst_head,
-        const char *hostname)
+        const char *hostname,
+        GError **error)
 {
 	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
 	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
-	GVariantBuilder servers;
-	const NMDnsIPConfigData *ip_data;
-
-	start_dnsmasq (self);
-
-	g_variant_builder_init (&servers, G_VARIANT_TYPE ("aas"));
 
-	if (global_config)
-		add_global_config (self, &servers, global_config);
-	else {
-		c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst)
-			add_ip_config (self, &servers, ip_data);
-	}
+	if (!start_dnsmasq (self, TRUE, error))
+		return FALSE;
 
-	g_clear_pointer (&priv->set_server_ex_args, g_variant_unref);
-	priv->set_server_ex_args = g_variant_ref_sink (g_variant_new ("(aas)", &servers));
+	nm_clear_pointer (&priv->set_server_ex_args, g_variant_unref);
+	priv->set_server_ex_args = g_variant_ref_sink (create_update_args (self,
+	                                                                   global_config,
+	                                                                   ip_config_lst_head,
+	                                                                   hostname));
 
 	send_dnsmasq_update (self);
-
 	return TRUE;
 }
 
 /*****************************************************************************/
 
 static void
-child_quit (NMDnsPlugin *plugin, int status)
+stop (NMDnsPlugin *plugin)
 {
 	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
 	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
-	gboolean failed = TRUE;
-	int err;
-
-	if (WIFEXITED (status)) {
-		err = WEXITSTATUS (status);
-		if (err) {
-			_LOGW ("dnsmasq exited with error: %s",
-			       nm_utils_dnsmasq_status_to_string (err, NULL, 0));
-		} else {
-			_LOGD ("dnsmasq exited normally");
-			failed = FALSE;
-		}
-	} else if (WIFSTOPPED (status))
-		_LOGW ("dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
-	else if (WIFSIGNALED (status))
-		_LOGW ("dnsmasq died with signal %d", WTERMSIG (status));
-	else
-		_LOGW ("dnsmasq died from an unknown cause");
-
-	priv->running = FALSE;
-
-	if (failed)
-		g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
-}
-
-/*****************************************************************************/
 
-static gboolean
-is_caching (NMDnsPlugin *plugin)
-{
-	return TRUE;
-}
+	priv->is_stopped = TRUE;
+	priv->burst_start_at = 0;
+	nm_clear_g_source (&priv->burst_retry_timeout_id);
 
-static const char *
-get_name (NMDnsPlugin *plugin)
-{
-	return "dnsmasq";
+	/* Cancelling the cancellable will also terminate the
+	 * process (in the background). */
+	_main_cleanup (self, FALSE);
 }
 
 /*****************************************************************************/
@@ -466,16 +1152,20 @@ nm_dns_dnsmasq_new (void)
 static void
 dispose (GObject *object)
 {
-	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE ((NMDnsDnsmasq *) object);
+	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (object);
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
 
-	nm_clear_g_cancellable (&priv->dnsmasq_cancellable);
-	nm_clear_g_cancellable (&priv->update_cancellable);
+	priv->is_stopped = TRUE;
 
-	g_clear_object (&priv->dnsmasq);
+	nm_clear_g_source (&priv->burst_retry_timeout_id);
+
+	_main_cleanup (self, FALSE);
 
 	g_clear_pointer (&priv->set_server_ex_args, g_variant_unref);
 
 	G_OBJECT_CLASS (nm_dns_dnsmasq_parent_class)->dispose (object);
+
+	g_clear_object (&priv->dbus_connection);
 }
 
 static void
@@ -486,8 +1176,8 @@ nm_dns_dnsmasq_class_init (NMDnsDnsmasqClass *dns_class)
 
 	object_class->dispose = dispose;
 
-	plugin_class->child_quit = child_quit;
-	plugin_class->is_caching = is_caching;
-	plugin_class->update = update;
-	plugin_class->get_name = get_name;
+	plugin_class->plugin_name = "dnsmasq";
+	plugin_class->is_caching  = TRUE;
+	plugin_class->stop        = stop;
+	plugin_class->update      = update;
 }
diff --git a/src/dns/nm-dns-dnsmasq.h b/src/dns/nm-dns-dnsmasq.h
index 7623d167..579e7686 100644
--- a/src/dns/nm-dns-dnsmasq.h
+++ b/src/dns/nm-dns-dnsmasq.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 Red Hat, Inc.
  */
 
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index 2c55bee9..c7ca0b47 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -1,23 +1,8 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
  * Copyright (C) 2004 - 2017 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
- *   and others
  */
 
 #include "nm-default.h"
@@ -63,10 +48,6 @@
 #define NETCONFIG_PATH "/sbin/netconfig"
 #endif
 
-#define PLUGIN_RATELIMIT_INTERVAL    30
-#define PLUGIN_RATELIMIT_BURST       5
-#define PLUGIN_RATELIMIT_DELAY       300
-
 /*****************************************************************************/
 
 typedef enum {
@@ -1441,13 +1422,15 @@ update_dns (NMDnsManager *self,
 		nm_dns_plugin_update (priv->sd_resolve_plugin,
 		                      global_config,
 		                      _ip_config_lst_head (self),
-		                      priv->hostname);
+		                      priv->hostname,
+		                      NULL);
 	}
 
 	/* Let any plugins do their thing first */
 	if (priv->plugin) {
 		NMDnsPlugin *plugin = priv->plugin;
 		const char *plugin_name = nm_dns_plugin_get_name (plugin);
+		gs_free_error GError *plugin_error = NULL;
 
 		if (nm_dns_plugin_is_caching (plugin)) {
 			if (no_caching) {
@@ -1462,8 +1445,9 @@ update_dns (NMDnsManager *self,
 		if (!nm_dns_plugin_update (plugin,
 		                           global_config,
 		                           _ip_config_lst_head (self),
-		                           priv->hostname)) {
-			_LOGW ("update-dns: plugin %s update failed", plugin_name);
+		                           priv->hostname,
+		                           &plugin_error)) {
+			_LOGW ("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message);
 
 			/* If the plugin failed to update, we shouldn't write out a local
 			 * caching DNS configuration to resolv.conf.
@@ -1567,66 +1551,7 @@ update_dns (NMDnsManager *self,
 	return !update || result == SR_SUCCESS;
 }
 
-static void
-plugin_failed (NMDnsPlugin *plugin, gpointer user_data)
-{
-	NMDnsManager *self = NM_DNS_MANAGER (user_data);
-	GError *error = NULL;
-
-	/* Errors with non-caching plugins aren't fatal */
-	if (!nm_dns_plugin_is_caching (plugin))
-		return;
-
-	/* Disable caching until the next DNS update */
-	if (!update_dns (self, TRUE, &error)) {
-		_LOGW ("could not commit DNS changes: %s", error->message);
-		g_clear_error (&error);
-	}
-}
-
-static gboolean
-plugin_child_quit_update_dns (gpointer user_data)
-{
-	GError *error = NULL;
-	NMDnsManager *self = NM_DNS_MANAGER (user_data);
-
-	/* Let the plugin try to spawn the child again */
-	if (!update_dns (self, FALSE, &error)) {
-		_LOGW ("could not commit DNS changes: %s", error->message);
-		g_clear_error (&error);
-	}
-
-	return G_SOURCE_REMOVE;
-}
-
-static void
-plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data)
-{
-	NMDnsManager *self = NM_DNS_MANAGER (user_data);
-	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	gint64 ts = nm_utils_get_monotonic_timestamp_ms ();
-
-	_LOGW ("plugin %s child quit unexpectedly", nm_dns_plugin_get_name (plugin));
-
-	if (   !priv->plugin_ratelimit.ts
-	    || (ts - priv->plugin_ratelimit.ts) / 1000 > PLUGIN_RATELIMIT_INTERVAL) {
-		priv->plugin_ratelimit.ts = ts;
-		priv->plugin_ratelimit.num_restarts = 0;
-	} else {
-		priv->plugin_ratelimit.num_restarts++;
-		if (priv->plugin_ratelimit.num_restarts > PLUGIN_RATELIMIT_BURST) {
-			plugin_failed (plugin, self);
-			_LOGW ("plugin %s child respawning too fast, delaying update for %u seconds",
-			        nm_dns_plugin_get_name (plugin), PLUGIN_RATELIMIT_DELAY);
-			priv->plugin_ratelimit.timer = g_timeout_add_seconds (PLUGIN_RATELIMIT_DELAY,
-			                                                      plugin_child_quit_update_dns,
-			                                                      self);
-			return;
-		}
-	}
-
-	plugin_child_quit_update_dns (self);
-}
+/*****************************************************************************/
 
 static void
 _ip_config_dns_priority_changed (gpointer config,
@@ -1856,15 +1781,14 @@ _clear_plugin (NMDnsManager *self)
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
+	priv->plugin_ratelimit.ts = 0;
+	nm_clear_g_source (&priv->plugin_ratelimit.timer);
+
 	if (priv->plugin) {
-		g_signal_handlers_disconnect_by_func (priv->plugin, plugin_failed, self);
-		g_signal_handlers_disconnect_by_func (priv->plugin, plugin_child_quit, self);
 		nm_dns_plugin_stop (priv->plugin);
 		g_clear_object (&priv->plugin);
 		return TRUE;
 	}
-	priv->plugin_ratelimit.ts = 0;
-	nm_clear_g_source (&priv->plugin_ratelimit.timer);
 	return FALSE;
 }
 
@@ -2083,12 +2007,6 @@ again:
 	} else if (nm_clear_g_object (&priv->sd_resolve_plugin))
 		systemd_resolved_changed = TRUE;
 
-	if (   plugin_changed
-	    && priv->plugin) {
-		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self);
-		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self);
-	}
-
 	g_object_freeze_notify (G_OBJECT (self));
 
 	if (!nm_streq0 (priv->mode, mode)) {
diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h
index 1cb2c669..2a0c9dee 100644
--- a/src/dns/nm-dns-manager.h
+++ b/src/dns/nm-dns-manager.h
@@ -1,23 +1,8 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
  * Copyright (C) 2004 - 2013 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
- *   and others
  */
 
 #ifndef __NETWORKMANAGER_DNS_MANAGER_H__
diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c
index 582abc62..c8876f1e 100644
--- a/src/dns/nm-dns-plugin.c
+++ b/src/dns/nm-dns-plugin.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 - 2012 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -30,14 +17,6 @@
 
 /*****************************************************************************/
 
-enum {
-	FAILED,
-	CHILD_QUIT,
-	LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
 typedef struct _NMDnsPluginPrivate {
 	GPid pid;
 	guint watch_id;
@@ -45,7 +24,7 @@ typedef struct _NMDnsPluginPrivate {
 	char *pidfile;
 } NMDnsPluginPrivate;
 
-G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
+G_DEFINE_ABSTRACT_TYPE (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT)
 
 #define NM_DNS_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMDnsPlugin, NM_IS_DNS_PLUGIN)
 
@@ -77,188 +56,46 @@ gboolean
 nm_dns_plugin_update (NMDnsPlugin *self,
                       const NMGlobalDnsConfig *global_config,
                       const CList *ip_config_lst_head,
-                      const char *hostname)
+                      const char *hostname,
+                      GError **error)
 {
 	g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE);
 
 	return NM_DNS_PLUGIN_GET_CLASS (self)->update (self,
 	                                               global_config,
 	                                               ip_config_lst_head,
-	                                               hostname);
-}
-
-static gboolean
-is_caching (NMDnsPlugin *self)
-{
-	return FALSE;
+	                                               hostname,
+	                                               error);
 }
 
 gboolean
 nm_dns_plugin_is_caching (NMDnsPlugin *self)
 {
-	return NM_DNS_PLUGIN_GET_CLASS (self)->is_caching (self);
+	return NM_DNS_PLUGIN_GET_CLASS (self)->is_caching;
 }
 
 const char *
 nm_dns_plugin_get_name (NMDnsPlugin *self)
 {
-	g_assert (NM_DNS_PLUGIN_GET_CLASS (self)->get_name);
-	return NM_DNS_PLUGIN_GET_CLASS (self)->get_name (self);
-}
+	NMDnsPluginClass *klass;
 
-/*****************************************************************************/
+	g_return_val_if_fail (NM_IS_DNS_PLUGIN (self), NULL);
 
-static void
-_clear_pidfile (NMDnsPlugin *self)
-{
-	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
-
-	if (priv->pidfile) {
-		unlink (priv->pidfile);
-		g_clear_pointer (&priv->pidfile, g_free);
-	}
-}
-
-static void
-kill_existing (const char *progname, const char *pidfile, const char *kill_match)
-{
-	long pid;
-	gs_free char *contents = NULL;
-	gs_free char *cmdline_contents = NULL;
-	guint64 start_time;
-	char proc_path[256];
-	gs_free_error GError *error = NULL;
-
-	if (!pidfile)
-		return;
-
-	if (!kill_match)
-		g_return_if_reached ();
-
-	if (!g_file_get_contents (pidfile, &contents, NULL, &error)) {
-		if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
-			return;
-		goto out;
-	}
-
-	pid = _nm_utils_ascii_str_to_int64 (contents, 10, 2, INT_MAX, -1);
-	if (pid == -1)
-		goto out;
-
-	start_time = nm_utils_get_start_time_for_pid (pid, NULL, NULL);
-	if (start_time == 0)
-		goto out;
-
-	nm_sprintf_buf (proc_path, "/proc/%ld/cmdline", pid);
-	if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
-		goto out;
-
-	if (!strstr (cmdline_contents, kill_match))
-		goto out;
-
-	nm_utils_kill_process_sync (pid, start_time, SIGKILL, _NMLOG_DOMAIN,
-	                            progname ?: "<dns-process>",
-	                            0, 0, 1000);
-
-out:
-	unlink (pidfile);
-}
-
-static void
-watch_cb (GPid pid, int status, gpointer user_data)
-{
-	NMDnsPlugin *self = NM_DNS_PLUGIN (user_data);
-	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
-
-	priv->pid = 0;
-	priv->watch_id = 0;
-	g_clear_pointer (&priv->progname, g_free);
-	_clear_pidfile (self);
-
-	g_signal_emit (self, signals[CHILD_QUIT], 0, status);
-}
-
-GPid
-nm_dns_plugin_child_pid (NMDnsPlugin *self)
-{
-	NMDnsPluginPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_DNS_PLUGIN (self), 0);
-
-	priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
-	return priv->pid;
-}
-
-GPid
-nm_dns_plugin_child_spawn (NMDnsPlugin *self,
-                           const char **argv,
-                           const char *pidfile,
-                           const char *kill_match)
-{
-	NMDnsPluginPrivate *priv;
-	GError *error = NULL;
-	GPid pid;
-	gs_free char *cmdline = NULL;
-	gs_free char *progname = NULL;
-
-	g_return_val_if_fail (argv && argv[0], 0);
-	g_return_val_if_fail (NM_IS_DNS_PLUGIN (self), 0);
-
-	priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
-
-	g_return_val_if_fail (!priv->pid, 0);
-	nm_assert (!priv->progname);
-	nm_assert (!priv->watch_id);
-	nm_assert (!priv->pidfile);
-
-	progname = g_path_get_basename (argv[0]);
-	kill_existing (progname, pidfile, kill_match);
-
-	_LOGI ("starting %s...", progname);
-	_LOGD ("command line: %s",
-	       (cmdline = g_strjoinv (" ", (char **) argv)));
-
-	if (!g_spawn_async (NULL, (char **) argv, NULL,
-	                   G_SPAWN_DO_NOT_REAP_CHILD,
-	                   nm_utils_setpgid, NULL,
-	                   &pid,
-	                   &error)) {
-		_LOGW ("failed to spawn %s: %s",
-		       progname, error->message);
-		g_clear_error (&error);
-		return 0;
-	}
-
-	_LOGD ("%s started with pid %d", progname, pid);
-	priv->watch_id = g_child_watch_add (pid, (GChildWatchFunc) watch_cb, self);
-	priv->pid = pid;
-	priv->progname = g_steal_pointer (&progname);
-	priv->pidfile = g_strdup (pidfile);
-
-	return pid;
-}
-
-gboolean
-nm_dns_plugin_child_kill (NMDnsPlugin *self)
-{
-	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
-
-	nm_clear_g_source (&priv->watch_id);
-	if (priv->pid) {
-		nm_utils_kill_child_sync (priv->pid, SIGTERM, _NMLOG_DOMAIN,
-		                          priv->progname ?: "<dns-process>", NULL, 1000, 0);
-		priv->pid = 0;
-		g_clear_pointer (&priv->progname, g_free);
-	}
-	_clear_pidfile (self);
-
-	return TRUE;
+	klass = NM_DNS_PLUGIN_GET_CLASS (self);
+	nm_assert (klass->plugin_name);
+	return klass->plugin_name;
 }
 
 void
 nm_dns_plugin_stop (NMDnsPlugin *self)
 {
-	nm_dns_plugin_child_kill (self);
+	NMDnsPluginClass *klass;
+
+	g_return_if_fail (NM_IS_DNS_PLUGIN (self));
+
+	klass = NM_DNS_PLUGIN_GET_CLASS (self);
+	if (klass->stop)
+		klass->stop (self);
 }
 
 /*****************************************************************************/
@@ -266,49 +103,9 @@ nm_dns_plugin_stop (NMDnsPlugin *self)
 static void
 nm_dns_plugin_init (NMDnsPlugin *self)
 {
-	self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DNS_PLUGIN, NMDnsPluginPrivate);
-}
-
-static void
-dispose (GObject *object)
-{
-	NMDnsPlugin *self = NM_DNS_PLUGIN (object);
-
-	nm_dns_plugin_stop (self);
-
-	G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object);
 }
 
 static void
 nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
-
-	g_type_class_add_private (plugin_class, sizeof (NMDnsPluginPrivate));
-
-	object_class->dispose = dispose;
-
-	plugin_class->is_caching = is_caching;
-
-	/* Emitted by the plugin and consumed by NMDnsManager when
-	 * some error happens with the nameserver subprocess.  Causes NM to fall
-	 * back to writing out a non-local-caching resolv.conf until the next
-	 * DNS update.
-	 */
-	signals[FAILED] =
-	    g_signal_new (NM_DNS_PLUGIN_FAILED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL,
-	                  g_cclosure_marshal_VOID__VOID,
-	                  G_TYPE_NONE, 0);
-
-	signals[CHILD_QUIT] =
-	    g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMDnsPluginClass, child_quit),
-	                  NULL, NULL,
-	                  g_cclosure_marshal_VOID__INT,
-	                  G_TYPE_NONE, 1, G_TYPE_INT);
 }
diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h
index 98bda7cf..78823739 100644
--- a/src/dns/nm-dns-plugin.h
+++ b/src/dns/nm-dns-plugin.h
@@ -1,22 +1,10 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 Red Hat, Inc.
  */
 
-#ifndef __NETWORKMANAGER_DNS_PLUGIN_H__
-#define __NETWORKMANAGER_DNS_PLUGIN_H__
+#ifndef __NM_DNS_PLUGIN_H__
+#define __NM_DNS_PLUGIN_H__
 
 #include "nm-dns-manager.h"
 #include "nm-config-data.h"
@@ -28,21 +16,13 @@
 #define NM_IS_DNS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DNS_PLUGIN))
 #define NM_DNS_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass))
 
-#define NM_DNS_PLUGIN_FAILED "failed"
-#define NM_DNS_PLUGIN_CHILD_QUIT "child-quit"
-
-struct _NMDnsPluginPrivate;
-
 typedef struct {
 	GObject parent;
-	struct _NMDnsPluginPrivate *_priv;
 } NMDnsPlugin;
 
 typedef struct {
 	GObjectClass parent;
 
-	/* Methods */
-
 	/* Called when DNS information is changed.  'configs' is an array
 	 * of pointers to NMDnsIPConfigData sorted by priority.
 	 * 'global_config' is the optional global DNS
@@ -51,25 +31,19 @@ typedef struct {
 	gboolean (*update) (NMDnsPlugin *self,
 	                    const NMGlobalDnsConfig *global_config,
 	                    const CList *ip_config_lst_head,
-	                    const char *hostname);
+	                    const char *hostname,
+	                    GError **error);
 
-	/* Subclasses should override and return TRUE if they start a local
-	 * caching nameserver that listens on localhost and would block any
-	 * other local caching nameserver from operating.
-	 */
-	gboolean (*is_caching) (NMDnsPlugin *self);
-
-	/* Subclasses should override this and return their plugin name */
-	const char *(*get_name) (NMDnsPlugin *self);
+	void (*stop) (NMDnsPlugin *self);
 
-	/* Signals */
+	const char *plugin_name;
 
-	/* Emitted by the plugin base class when the nameserver subprocess
-	 * quits.  This signal is consumed by the plugin subclasses and not
-	 * by NMDnsManager.  If the subclass decides the exit status (as returned
-	 * by waitpid(2)) is fatal it should then emit the 'failed' signal.
+	/* Types should set to TRUE if they start a local caching nameserver
+	 * that listens on localhost and would block any other local caching
+	 * nameserver from operating.
 	 */
-	void (*child_quit) (NMDnsPlugin *self, int status);
+	bool is_caching:1;
+
 } NMDnsPluginClass;
 
 GType nm_dns_plugin_get_type (void);
@@ -81,25 +55,9 @@ const char *nm_dns_plugin_get_name (NMDnsPlugin *self);
 gboolean nm_dns_plugin_update (NMDnsPlugin *self,
                                const NMGlobalDnsConfig *global_config,
                                const CList *ip_config_lst_head,
-                               const char *hostname);
+                               const char *hostname,
+                               GError **error);
 
 void nm_dns_plugin_stop (NMDnsPlugin *self);
 
-/* For subclasses/plugins */
-
-/* Spawn a child process and watch for it to quit.  'argv' is the NULL-terminated
- * argument vector to spawn the child with, where argv[0] is the full path to
- * the child's executable.  If 'pidfile' is given the process owning the PID
- * contained in 'pidfile' will be killed if its command line matches 'kill_match'
- * and the pidfile will be deleted.
- */
-GPid nm_dns_plugin_child_spawn (NMDnsPlugin *self,
-                                const char **argv,
-                                const char *pidfile,
-                                const char *kill_match);
-
-GPid nm_dns_plugin_child_pid (NMDnsPlugin *self);
-
-gboolean nm_dns_plugin_child_kill (NMDnsPlugin *self);
-
-#endif /* __NETWORKMANAGER_DNS_PLUGIN_H__ */
+#endif /* __NM_DNS_PLUGIN_H__ */
diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 69b3e45e..4ab13a91 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
  * Copyright (C) 2016 Sjoerd Simons <sjoerd@luon.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  */
 
 #include "nm-default.h"
@@ -351,7 +337,8 @@ static gboolean
 update (NMDnsPlugin *plugin,
         const NMGlobalDnsConfig *global_config,
         const CList *ip_config_lst_head,
-        const char *hostname)
+        const char *hostname,
+        GError **error)
 {
 	NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED (plugin);
 	gs_unref_hashtable GHashTable *interfaces = NULL;
@@ -401,20 +388,6 @@ update (NMDnsPlugin *plugin,
 
 /*****************************************************************************/
 
-static gboolean
-is_caching (NMDnsPlugin *plugin)
-{
-	return TRUE;
-}
-
-static const char *
-get_name (NMDnsPlugin *plugin)
-{
-	return "systemd-resolved";
-}
-
-/*****************************************************************************/
-
 static void
 name_owner_changed (NMDnsSystemdResolved *self,
                     const char *owner)
@@ -566,7 +539,7 @@ nm_dns_systemd_resolved_class_init (NMDnsSystemdResolvedClass *dns_class)
 
 	object_class->dispose = dispose;
 
-	plugin_class->is_caching = is_caching;
-	plugin_class->update = update;
-	plugin_class->get_name = get_name;
+	plugin_class->plugin_name = "systemd-resolved";
+	plugin_class->is_caching  = TRUE;
+	plugin_class->update      = update;
 }
diff --git a/src/dns/nm-dns-systemd-resolved.h b/src/dns/nm-dns-systemd-resolved.h
index 14e31e2b..31698519 100644
--- a/src/dns/nm-dns-systemd-resolved.h
+++ b/src/dns/nm-dns-systemd-resolved.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 Red Hat, Inc.
  * Copyright (C) 2016 Sjoerd Simons <sjoerd@luon.net>
  */
diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c
index 9e10950a..ca681f37 100644
--- a/src/dns/nm-dns-unbound.c
+++ b/src/dns/nm-dns-unbound.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2014 Red Hat, Inc.
  * Author: Pavel Å imerda <psimerda@redhat.com>
  */
+
 #include "nm-default.h"
 
 #include "nm-dns-unbound.h"
@@ -40,9 +28,11 @@ static gboolean
 update (NMDnsPlugin *plugin,
         const NMGlobalDnsConfig *global_config,
         const CList *ip_config_lst_head,
-        const char *hostname)
+        const char *hostname,
+        GError **error)
 {
 	char *argv[] = { DNSSEC_TRIGGER_PATH, "--async", "--update", NULL };
+	gs_free_error GError *local = NULL;
 	int status;
 
 	/* TODO: We currently call a script installed with the dnssec-trigger
@@ -54,23 +44,21 @@ update (NMDnsPlugin *plugin,
 	 * without calling custom scripts. The dnssec-trigger functionality
 	 * may be eventually merged into NetworkManager.
 	 */
-	if (!g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, NULL))
+	if (!g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &local)) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    "error spawning dns-trigger: %s",
+		                    local->message);
 		return FALSE;
-	return (status == 0);
-}
-
-static gboolean
-is_caching (NMDnsPlugin *plugin)
-{
+	}
+	if (status != 0) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    "dns-trigger exited with error code %d",
+		                    status);
+		return FALSE;
+	}
 	return TRUE;
 }
 
-static const char *
-get_name (NMDnsPlugin *plugin)
-{
-	return "unbound";
-}
-
 /*****************************************************************************/
 
 static void
@@ -89,7 +77,7 @@ nm_dns_unbound_class_init (NMDnsUnboundClass *klass)
 {
 	NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS (klass);
 
-	plugin_class->update = update;
-	plugin_class->is_caching = is_caching;
-	plugin_class->get_name = get_name;
+	plugin_class->plugin_name = "unbound";
+	plugin_class->is_caching  = TRUE;
+	plugin_class->update      = update;
 }
diff --git a/src/dns/nm-dns-unbound.h b/src/dns/nm-dns-unbound.h
index 743699ea..51737b16 100644
--- a/src/dns/nm-dns-unbound.h
+++ b/src/dns/nm-dns-unbound.h
@@ -1,19 +1,8 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
+
 #ifndef __NETWORKMANAGER_DNS_UNBOUND_H__
 #define __NETWORKMANAGER_DNS_UNBOUND_H__
 
diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c
index b60b2952..735605ee 100644
--- a/src/dnsmasq/nm-dnsmasq-manager.c
+++ b/src/dnsmasq/nm-dnsmasq-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2012 Red Hat, Inc.
  */
 
diff --git a/src/dnsmasq/nm-dnsmasq-manager.h b/src/dnsmasq/nm-dnsmasq-manager.h
index 7d4d9c9b..0a616b2a 100644
--- a/src/dnsmasq/nm-dnsmasq-manager.h
+++ b/src/dnsmasq/nm-dnsmasq-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
diff --git a/src/dnsmasq/nm-dnsmasq-utils.c b/src/dnsmasq/nm-dnsmasq-utils.c
index 089b8ce0..01d59ecc 100644
--- a/src/dnsmasq/nm-dnsmasq-utils.c
+++ b/src/dnsmasq/nm-dnsmasq-utils.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/dnsmasq/nm-dnsmasq-utils.h b/src/dnsmasq/nm-dnsmasq-utils.h
index 393e7236..a6a0342f 100644
--- a/src/dnsmasq/nm-dnsmasq-utils.h
+++ b/src/dnsmasq/nm-dnsmasq-utils.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/dnsmasq/tests/meson.build b/src/dnsmasq/tests/meson.build
index 09ef52e5..6e429899 100644
--- a/src/dnsmasq/tests/meson.build
+++ b/src/dnsmasq/tests/meson.build
@@ -3,7 +3,8 @@ test_unit = 'test-dnsmasq-utils'
 exe = executable(
   test_unit,
   test_unit + '.c',
-  dependencies: test_nm_dep,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
 )
 
 test(
diff --git a/src/dnsmasq/tests/test-dnsmasq-utils.c b/src/dnsmasq/tests/test-dnsmasq-utils.c
index 6d8e5a1f..432a5727 100644
--- a/src/dnsmasq/tests/test-dnsmasq-utils.c
+++ b/src/dnsmasq/tests/test-dnsmasq-utils.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2013 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/initrd/meson.build b/src/initrd/meson.build
index fa8c016b..09036f27 100644
--- a/src/initrd/meson.build
+++ b/src/initrd/meson.build
@@ -1,26 +1,29 @@
 sources = files(
   'nmi-cmdline-reader.c',
+  'nmi-dt-reader.c',
   'nmi-ibft-reader.c',
 )
 
-nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON']
-
 libnmi_core = static_library(
   'nmi-core',
-  c_args: nm_cflags,
   sources: sources,
-  include_directories: src_inc,
-  dependencies: libnm_core_dep,
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
 )
 
 name = 'nm-initrd-generator'
+
+links = [
+  libnetwork_manager_base,
+  libnmi_core,
+]
+
 executable(
   name,
   name + '.c',
-  c_args: nm_cflags,
-  include_directories: src_inc,
-  dependencies: [ libnm_core_dep ],
-  link_with: [libnetwork_manager_base, libnmi_core],
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
+  link_with: links,
   link_args: ldflags_linker_script_binary,
   link_depends: linker_script_binary,
   install: true,
diff --git a/src/initrd/nm-initrd-generator.c b/src/initrd/nm-initrd-generator.c
index 0ff3428e..f3f53acf 100644
--- a/src/initrd/nm-initrd-generator.c
+++ b/src/initrd/nm-initrd-generator.c
@@ -1,20 +1,5 @@
-/* NetworkManager initrd configuration generator
- *
- * 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
@@ -63,7 +48,12 @@ output_conn (gpointer key, gpointer value, gpointer user_data)
 		filename = nm_keyfile_utils_create_filename (basename, TRUE);
 		full_filename = g_build_filename (connections_dir, filename, NULL);
 
-		if (!nm_utils_file_set_contents (full_filename, data, len, 0600, &error))
+		if (!nm_utils_file_set_contents (full_filename,
+		                                 data,
+		                                 len,
+		                                 0600,
+		                                 NULL,
+		                                 &error))
 			goto err_out;
 	} else
 		g_print ("\n*** Connection '%s' ***\n\n%s", basename, data);
diff --git a/src/initrd/nm-initrd-generator.h b/src/initrd/nm-initrd-generator.h
index 7ff9a92e..cba383d1 100644
--- a/src/initrd/nm-initrd-generator.h
+++ b/src/initrd/nm-initrd-generator.h
@@ -1,20 +1,5 @@
-/* NetworkManager initrd configuration generator
- *
- * 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2014, 2018 Red Hat, Inc.
  */
 
@@ -41,6 +26,8 @@ GHashTable *nmi_ibft_read (const char *sysfs_dir);
 
 gboolean nmi_ibft_update_connection_from_nic (NMConnection *connection, GHashTable *nic, GError **error);
 
+NMConnection *nmi_dt_reader_parse (const char *sysfs_dir);
+
 GHashTable *nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv);
 
 #endif  /* __NM_INITRD_GENERATOR_H__ */
diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c
index c305cd4d..c32ef1cf 100644
--- a/src/initrd/nmi-cmdline-reader.c
+++ b/src/initrd/nmi-cmdline-reader.c
@@ -1,20 +1,5 @@
-/* NetworkManager initrd configuration generator
- *
- * 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
@@ -74,6 +59,7 @@ add_conn (GHashTable *connections,
 	g_object_set (setting,
 	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
 	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+	              NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, (int) NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
 	              NULL);
 
 	setting = nm_setting_connection_new ();
@@ -270,7 +256,8 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 		}
 	}
 
-	if (ifname == NULL && g_strcmp0 (kind, "ibft") == 0) {
+	if (ifname == NULL && (   g_strcmp0 (kind, "fw") == 0
+	                       || g_strcmp0 (kind, "ibft") == 0)) {
 		GHashTableIter iter;
 		const char *mac;
 		GHashTable *nic;
@@ -302,6 +289,13 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 			                     connection);
 		}
 
+		connection = nmi_dt_reader_parse (sysfs_dir);
+		if (connection) {
+			g_hash_table_insert (connections,
+			                     g_strdup ("ofw"),
+			                     connection);
+		}
+
 		return;
 	}
 
diff --git a/src/initrd/nmi-dt-reader.c b/src/initrd/nmi-dt-reader.c
new file mode 100644
index 00000000..a8677ed9
--- /dev/null
+++ b/src/initrd/nmi-dt-reader.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-initrd-generator.h"
+
+#include <arpa/inet.h>
+
+#include "nm-core-internal.h"
+
+/*****************************************************************************/
+
+#define _NMLOG(level, domain, ...) \
+    nm_log ((level), (domain), NULL, NULL, \
+            "dt-reader: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__) \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+static gboolean
+dt_get_property (const char *base,
+                 const char *dev,
+                 const char *prop,
+                 char **contents,
+                 size_t *length)
+{
+	gs_free char *filename = g_build_filename (base, dev, prop, NULL);
+	gs_free_error GError *error = NULL;
+
+	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+		return FALSE;
+
+	if (!contents)
+		return TRUE;
+
+	if (!g_file_get_contents (filename, contents, length, &error)) {
+		_LOGW (LOGD_CORE, "%s: Can not read the %s property: %s",
+		       dev, prop, error->message);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static NMIPAddress *
+dt_get_ipaddr_property (const char *base,
+                        const char *dev,
+                        const char *prop,
+                        int *family)
+{
+	gs_free char *buf = NULL;
+	size_t len;
+	int f;
+
+	if (!dt_get_property (base, dev, prop, &buf, &len))
+		return NULL;
+
+	f = nm_utils_addr_family_from_size (len);
+	if (   f == AF_UNSPEC
+	    || (   *family != AF_UNSPEC
+	        && *family != f)) {
+		_LOGW (LOGD_CORE, "%s: Address %s has unrecognized length (%zd)",
+		       dev, prop, len);
+		return NULL;
+	}
+
+	*family = f;
+	return nm_ip_address_new_binary (f, buf, 0, NULL);
+}
+
+static char *
+dt_get_hwaddr_property (const char *base,
+                        const char *dev,
+                        const char *prop)
+{
+	gs_free guint8 *buf = NULL;
+	size_t len;
+
+	if (!dt_get_property (base, dev, prop, (char **) &buf, &len))
+		return NULL;
+
+	if (len != ETH_ALEN) {
+		_LOGW (LOGD_CORE, "%s: MAC address %s has unrecognized length (%zd)",
+		       dev, prop, len);
+		return NULL;
+	}
+
+	return g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x",
+	                        buf[0], buf[1], buf[2],
+	                        buf[3], buf[4], buf[4]);
+}
+
+static NMIPAddress *
+str_addr (const char *str, int *family)
+{
+	NMIPAddr addr_bin;
+
+	if (!nm_utils_parse_inaddr_bin_full (*family,
+	                                     TRUE,
+	                                     str,
+	                                     family,
+	                                     &addr_bin)) {
+		_LOGW (LOGD_CORE, "Malformed IP address: '%s'", str);
+		return NULL;
+	}
+	return nm_ip_address_new_binary (*family, &addr_bin, 0, NULL);
+}
+
+NMConnection *
+nmi_dt_reader_parse (const char *sysfs_dir)
+{
+	gs_unref_object NMConnection *connection = NULL;
+	gs_free char *base = NULL;
+	gs_free char *bootpath = NULL;
+	gs_strfreev char **tokens = NULL;
+	char *path = NULL;
+	gboolean bootp = FALSE;
+	const char *s_ipaddr = NULL;
+	const char *s_netmask = NULL;
+	const char *s_gateway = NULL;
+	nm_auto_unref_ip_address NMIPAddress *ipaddr = NULL;
+	nm_auto_unref_ip_address NMIPAddress *gateway = NULL;
+	const char *duplex = NULL;
+	gs_free char *hwaddr = NULL;
+	gs_free char *local_hwaddr = NULL;
+	gs_free char *hostname = NULL;
+	guint32 speed = 0;
+	int prefix = -1;
+	NMSettingIPConfig *s_ip = NULL;
+	NMSetting *s_ip4 = NULL;
+	NMSetting *s_ip6 = NULL;
+	NMSetting *s_wired = NULL;
+	int family = AF_UNSPEC;
+	int i = 0;
+	char *c;
+	gs_free_error GError *error = NULL;
+
+	base = g_build_filename (sysfs_dir, "firmware", "devicetree",
+	                         "base", NULL);
+
+	if (!dt_get_property (base, "chosen", "bootpath", &bootpath, NULL))
+		return NULL;
+
+	c = strchr (bootpath, ':');
+	if (c) {
+		*c = '\0';
+		path = c + 1;
+	} else {
+		path = "";
+	}
+
+	dt_get_property (base, "chosen", "client-name", &hostname, NULL);
+
+	local_hwaddr = dt_get_hwaddr_property (base, bootpath, "local-mac-address");
+	hwaddr = dt_get_hwaddr_property (base, bootpath, "mac-address");
+	if (g_strcmp0 (local_hwaddr, hwaddr) == 0)
+		g_clear_pointer (&local_hwaddr, g_free);
+
+	tokens = g_strsplit (path, ",", 0);
+
+	/*
+	 * Ethernet device settings. Defined by "Open Firmware,
+	 * Recommended Practice: Device Support Extensions, Version 1.0 [1]
+	 * [1] https://www.devicetree.org/open-firmware/practice/devicex/dse1_0a.ps
+	 */
+
+	for (i = 0; tokens[i]; i++) {
+		/* Skip these. They have magical meaning for OpenFirmware. */
+		if (   strcmp (tokens[i], "nfs") == 0
+		    || strcmp (tokens[i], "last") == 0)
+			continue;
+		if (strcmp (tokens[i], "promiscuous") == 0) {
+			/* Ignore. */
+			continue;
+		}
+
+		if (g_str_has_prefix (tokens[i], "speed=")) {
+			speed = _nm_utils_ascii_str_to_int64 (tokens[i] + 6,
+			                                      10, 0, G_MAXUINT32, 0);
+			continue;
+		}
+
+		if (g_str_has_prefix (tokens[i], "duplex=auto")) {
+			continue;
+		} else if (   g_str_has_prefix (tokens[i], "duplex=half")
+		           || g_str_has_prefix (tokens[i], "duplex=full")) {
+			duplex = tokens[i] + 7;
+			continue;
+		}
+
+		break;
+	}
+
+	/*
+	 * Network boot configuration. Defined by "Open Firmware,
+	 * Recommended Practice: TFTP Booting Extension, Version 1.0 [1]
+	 * [1] https://www.devicetree.org/open-firmware/practice/obp-tftp/tftp1_0.pdf
+	 */
+
+	for (; tokens[i]; i++) {
+		if (   strcmp (tokens[i], "bootp") == 0
+		    || strcmp (tokens[i], "dhcp") == 0
+		    || strcmp (tokens[i], "rarp") == 0) {
+			bootp = TRUE;
+			continue;
+		}
+		break;
+	}
+
+	/* s-iaddr, or perhaps a raw absolute filename */
+	if (tokens[i] && tokens[i][0] != '/')
+		i++;
+
+	/* filename */
+	if (tokens[i])
+		i++;
+
+	/* c-iaddr */
+	if (tokens[i]) {
+		s_ipaddr = tokens[i];
+		i++;
+	}
+
+	/* g-iaddr */
+	if (tokens[i]) {
+		s_gateway = tokens[i];
+		i++;
+	}
+
+	if (tokens[i] && (   strchr (tokens[i], '.')
+	                  || strchr (tokens[i], ':'))) {
+		/* yaboot claims the mask can be specified here,
+		 * though it doesn't support it. */
+		s_netmask = tokens[i];
+		i++;
+	}
+
+	/* bootp-retries */
+	if (tokens[i])
+		i++;
+
+	/* tftp-retries */
+	if (tokens[i])
+		i++;
+
+	if (tokens[i]) {
+		/* yaboot accepts a mask here */
+		s_netmask = tokens[i];
+		i++;
+	}
+
+	connection = nm_simple_connection_new ();
+
+	nm_connection_add_setting (connection,
+	                           g_object_new (NM_TYPE_SETTING_CONNECTION,
+	                                         NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	                                         NM_SETTING_CONNECTION_ID, "OpenFirmware Connection",
+	                                         NULL));
+
+	s_ip4 = nm_setting_ip4_config_new ();
+	nm_connection_add_setting (connection, s_ip4);
+
+	s_ip6 = nm_setting_ip6_config_new ();
+	nm_connection_add_setting (connection, s_ip6);
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, (int) NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+	              NULL);
+
+	if (!bootp && dt_get_property (base, "chosen", "bootp-response", NULL, NULL))
+		bootp = TRUE;
+
+	if (!bootp) {
+		nm_auto_unref_ip_address NMIPAddress *netmask = NULL;
+
+		netmask = dt_get_ipaddr_property (base, "chosen", "netmask-ip", &family);
+		gateway = dt_get_ipaddr_property (base, "chosen", "gateway-ip", &family);
+		if (gateway)
+			s_gateway = nm_ip_address_get_address (gateway);
+		ipaddr = dt_get_ipaddr_property (base, "chosen", "client-ip", &family);
+
+		if (family == AF_UNSPEC) {
+			nm_assert (netmask == NULL);
+			nm_assert (gateway == NULL);
+			nm_assert (ipaddr == NULL);
+
+			netmask = str_addr (s_netmask, &family);
+			ipaddr = str_addr (s_ipaddr, &family);
+
+			prefix = _nm_utils_ascii_str_to_int64 (s_netmask, 10, 0, 128, -1);
+		}
+
+		if (prefix == -1 && family == AF_INET && netmask) {
+			guint32 netmask_v4;
+
+			nm_ip_address_get_address_binary (netmask, &netmask_v4);
+			prefix = nm_utils_ip4_netmask_to_prefix (netmask_v4);
+		}
+
+		if (prefix == -1)
+			_LOGW (LOGD_CORE, "Unable to determine the network prefix");
+		else
+			nm_ip_address_set_prefix (ipaddr, prefix);
+	}
+
+	if (!ipaddr) {
+		family = AF_UNSPEC;
+		bootp = TRUE;
+	}
+
+	if (bootp) {
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+		              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname,
+		              NULL);
+		g_object_set (s_ip6,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+		              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname,
+		              NULL);
+	} else {
+		switch (family) {
+		case AF_INET:
+			s_ip = (NMSettingIPConfig *) s_ip4;
+			g_object_set (s_ip4,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+			              NULL);
+			g_object_set (s_ip6,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
+			              NULL);
+			break;
+		case AF_INET6:
+			s_ip = (NMSettingIPConfig *) s_ip6;
+			g_object_set (s_ip4,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+			              NULL);
+			g_object_set (s_ip6,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
+			              NULL);
+			break;
+		default:
+			g_return_val_if_reached (NULL);
+		}
+
+		nm_setting_ip_config_add_address (s_ip, ipaddr);
+		g_object_set (s_ip, NM_SETTING_IP_CONFIG_GATEWAY, s_gateway, NULL);
+	}
+
+	if (duplex || speed || hwaddr || local_hwaddr) {
+		s_wired = nm_setting_wired_new ();
+		nm_connection_add_setting (connection, s_wired);
+
+		g_object_set (s_wired,
+		              NM_SETTING_WIRED_SPEED, speed,
+		              NM_SETTING_WIRED_DUPLEX, duplex,
+		              NM_SETTING_WIRED_MAC_ADDRESS, hwaddr,
+		              NM_SETTING_WIRED_CLONED_MAC_ADDRESS, local_hwaddr,
+		              NULL);
+	}
+
+	if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
+		_LOGW (LOGD_CORE, "Generated an invalid connection: %s",
+		       error->message);
+		g_clear_pointer (&connection, g_object_unref);
+	}
+
+	return g_steal_pointer (&connection);
+}
diff --git a/src/initrd/nmi-ibft-reader.c b/src/initrd/nmi-ibft-reader.c
index 2db38e2c..ffce98fc 100644
--- a/src/initrd/nmi-ibft-reader.c
+++ b/src/initrd/nmi-ibft-reader.c
@@ -1,21 +1,6 @@
-/* NetworkManager initrd configuration generator
- *
- * 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 - 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2014 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -172,6 +157,10 @@ ip_setting_add_from_block (GHashTable *nic,
 	if (!s_ip6) {
 		s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
 		nm_connection_add_setting (connection, (NMSetting *) s_ip6);
+
+		g_object_set (s_ip6,
+		              NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, (int) NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+		              NULL);
 	}
 
 	family = guess_ip_address_family (s_ipaddr);
diff --git a/src/initrd/tests/meson.build b/src/initrd/tests/meson.build
index 20cf6af2..5dd2873c 100644
--- a/src/initrd/tests/meson.build
+++ b/src/initrd/tests/meson.build
@@ -1,20 +1,20 @@
+c_flags = test_c_flags + ['-DTEST_INITRD_DIR="@0@"'.format(meson.current_source_dir())]
+
 test_units = [
+  'test-dt-reader',
   'test-ibft-reader',
   'test-cmdline-reader',
 ]
 
-cflags = [
-  '-DTEST_INITRD_DIR="@0@"'.format(meson.current_source_dir()),
-]
-
 foreach test_unit : test_units
   exe = executable(
     test_unit,
     test_unit + '.c',
-    dependencies: test_nm_dep,
-    c_args: cflags,
+    dependencies: libnetwork_manager_test_dep,
+    c_args: c_flags,
     link_with: libnmi_core,
   )
+
   test(
     'initrd/' + test_unit,
     test_script,
diff --git a/src/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpath b/src/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpath
new file mode 100644
index 00000000..6f069ae0
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpath
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-request b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-request
new file mode 100644
index 00000000..034d423e
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-request
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-response b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-response
new file mode 100644
index 00000000..25982d6f
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-response
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpath b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpath
new file mode 100644
index 00000000..db880702
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpath
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip
new file mode 100644
index 00000000..7bde8641
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip
@@ -0,0 +1 @@
+ÿÿÿÿ
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip
new file mode 100644
index 00000000..f108ba9b
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip
@@ -0,0 +1,2 @@
+
++
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-name b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-name
new file mode 100644
index 00000000..fe0f76b8
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-name
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-name b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-name
new file mode 100644
index 00000000..b34f9f96
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-name
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip
new file mode 100644
index 00000000..f0361835
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip
@@ -0,0 +1,2 @@
+
++þ
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/name b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/name
new file mode 100644
index 00000000..f3e58052
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/name
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ip b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ip
new file mode 100644
index 00000000..d441cd83
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ip
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-path b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-path
new file mode 100644
index 00000000..f76dd238
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-path
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip
new file mode 100644
index 00000000..a2a4b2e8
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip
@@ -0,0 +1,2 @@
+
+&
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-file b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-file
new file mode 100644
index 00000000..c0e0e330
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-file
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-options b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-options
new file mode 100644
index 00000000..f76dd238
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-options
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_type b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_type
new file mode 100644
index 00000000..df3c9d9f
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_type
Binary files differdiff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address
new file mode 100644
index 00000000..c983e752
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address
@@ -0,0 +1 @@
+¬>åØ
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address
new file mode 100644
index 00000000..c983e752
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address
@@ -0,0 +1 @@
+¬>åØ
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/name b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/name
new file mode 100644
index 00000000..88eaddfe
--- /dev/null
+++ b/src/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/name
Binary files differdiff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c
index a28c634d..1d4bb9a6 100644
--- a/src/initrd/tests/test-cmdline-reader.c
+++ b/src/initrd/tests/test-cmdline-reader.c
@@ -1,21 +1,6 @@
-/* NetworkManager initrd configuration generator
- *
- * 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 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/initrd/tests/test-dt-reader.c b/src/initrd/tests/test-dt-reader.c
new file mode 100644
index 00000000..9a4dc735
--- /dev/null
+++ b/src/initrd/tests/test-dt-reader.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2014 - 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include "nm-core-internal.h"
+#include "NetworkManagerUtils.h"
+
+#include "../nm-initrd-generator.h"
+
+#include "nm-test-utils-core.h"
+
+static void
+test_read_dt_ofw (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	const char *mac_address;
+
+	connection = nmi_dt_reader_parse (TEST_INITRD_DIR "/sysfs-dt");
+	g_assert (connection);
+	nmtst_assert_connection_verifies (connection);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "OpenFirmware Connection");
+	g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
+	g_assert (nm_setting_connection_get_autoconnect (s_con));
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	mac_address = nm_setting_wired_get_mac_address (s_wired);
+	g_assert (mac_address);
+	g_assert (nm_utils_hwaddr_matches (mac_address, -1, "ac:7f:3e:e5:d8:d8", -1));
+	g_assert (!nm_setting_wired_get_duplex (s_wired));
+	g_assert_cmpint (nm_setting_wired_get_speed (s_wired), ==, 0);
+	g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	g_assert (s_ip4);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+	g_assert_cmpstr (nm_setting_ip_config_get_dhcp_hostname (s_ip4), ==, "demiurge");
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (s_ip6);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+
+	g_object_unref (connection);
+}
+
+static void
+test_read_dt_slof (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMIPAddress *ip4_addr;
+
+	connection = nmi_dt_reader_parse (TEST_INITRD_DIR "/sysfs-dt-tftp");
+	g_assert (connection);
+	nmtst_assert_connection_verifies (connection);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "OpenFirmware Connection");
+	g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
+	g_assert (nm_setting_connection_get_autoconnect (s_con));
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	g_assert (!nm_setting_wired_get_mac_address (s_wired));
+	g_assert_cmpstr (nm_setting_wired_get_duplex (s_wired), ==, "half");
+	g_assert_cmpint (nm_setting_wired_get_speed (s_wired), ==, 10);
+	g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	g_assert (s_ip4);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
+
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1);
+	ip4_addr = nm_setting_ip_config_get_address (s_ip4, 0);
+	g_assert (ip4_addr);
+	g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.32.2");
+	g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 16);
+
+	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.32.1");
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (s_ip6);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
+
+	g_object_unref (connection);
+}
+
+static void
+test_read_dt_none (void)
+{
+	NMConnection *connection;
+
+	connection = nmi_dt_reader_parse (TEST_INITRD_DIR "/sysfs");
+	g_assert (!connection);
+}
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
+
+	g_test_add_func ("/initrd/dt/ofw", test_read_dt_ofw);
+	g_test_add_func ("/initrd/dt/slof", test_read_dt_slof);
+	g_test_add_func ("/initrd/dt/none", test_read_dt_none);
+
+	return g_test_run ();
+}
diff --git a/src/initrd/tests/test-ibft-reader.c b/src/initrd/tests/test-ibft-reader.c
index a49d0abf..932c1a48 100644
--- a/src/initrd/tests/test-ibft-reader.c
+++ b/src/initrd/tests/test-ibft-reader.c
@@ -1,21 +1,6 @@
-/* NetworkManager initrd configuration generator
- *
- * 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 - 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2014 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/main-utils.c b/src/main-utils.c
index 38fd519a..80222495 100644
--- a/src/main-utils.c
+++ b/src/main-utils.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2012 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
diff --git a/src/main-utils.h b/src/main-utils.h
index d4da6052..58240686 100644
--- a/src/main-utils.h
+++ b/src/main-utils.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
diff --git a/src/main.c b/src/main.c
index f1e9690f..262b6484 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2017 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
@@ -102,7 +88,7 @@ _init_nm_debug (NMConfig *config)
 	debug = nm_config_data_get_value (nm_config_get_data_orig (config),
 	                                  NM_CONFIG_KEYFILE_GROUP_MAIN,
 	                                  NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG,
-	                                  NM_MANAGER_RELOAD_FLAGS_NONE);
+	                                  NM_CONFIG_GET_VALUE_NONE);
 
 	flags  = nm_utils_parse_debug_string (env, keys, G_N_ELEMENTS (keys));
 	flags |= nm_utils_parse_debug_string (debug, keys, G_N_ELEMENTS (keys));
@@ -168,6 +154,7 @@ print_config (NMConfigCmdLineOptions *config_cli)
 	gs_unref_object NMConfig *config = NULL;
 	gs_free_error GError *error = NULL;
 	NMConfigData *config_data;
+	const char *const*warnings;
 
 	nm_logging_setup ("OFF", "ALL", NULL, NULL);
 
@@ -179,7 +166,14 @@ print_config (NMConfigCmdLineOptions *config_cli)
 
 	config_data = nm_config_get_data (config);
 	fprintf (stdout, "# NetworkManager configuration: %s\n", nm_config_data_get_config_description (config_data));
-	nm_config_data_log (config_data, "", "", stdout);
+	nm_config_data_log (config_data, "", "", nm_config_get_no_auto_default_file (config), stdout);
+
+	warnings = nm_config_get_warnings (config);
+	if (warnings && warnings[0])
+		fprintf (stdout, "\n");
+	for ( ; warnings && warnings[0]; warnings++)
+		fprintf (stdout, "# WARNING: %s\n", warnings[0]);
+
 	return 0;
 }
 
@@ -386,7 +380,7 @@ main (int argc, char *argv[])
 	             nm_config_get_first_start (config) ? "for the first time" : "after a restart");
 
 	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);
+	nm_config_data_log (nm_config_get_data (config), "CONFIG: ", "  ", nm_config_get_no_auto_default_file (config), NULL);
 
 	if (error_invalid_logging_config) {
 		nm_log_warn (LOGD_CORE, "config: invalid logging configuration: %s", error_invalid_logging_config->message);
@@ -424,10 +418,7 @@ main (int argc, char *argv[])
 
 	NM_UTILS_KEEP_ALIVE (config, nm_netns_get (), "NMConfig-depends-on-NMNetns");
 
-	nm_auth_manager_setup (nm_config_data_get_value_boolean (nm_config_get_data_orig (config),
-	                                                         NM_CONFIG_KEYFILE_GROUP_MAIN,
-	                                                         NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
-	                                                         NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL));
+	nm_auth_manager_setup (nm_config_data_get_main_auth_polkit (nm_config_get_data_orig (config)));
 
 	manager = nm_manager_setup ();
 
diff --git a/src/meson.build b/src/meson.build
index f3f5ee58..748fa519 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,5 +1,11 @@
 src_inc = include_directories('.')
 
+daemon_nm_default_dep = declare_dependency(
+  sources: libnm_core_enum_sources[1],
+  include_directories: src_inc,
+  dependencies: libnm_core_nm_default_dep,
+)
+
 install_data(
   'org.freedesktop.NetworkManager.conf',
   install_dir: dbus_conf_dir,
@@ -9,15 +15,7 @@ subdir('systemd')
 
 core_plugins = []
 
-nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON']
-
-nm_dep = declare_dependency(
-  include_directories: src_inc,
-  dependencies: libnm_core_dep,
-  compile_args: nm_cflags,
-)
-
-cflags = nm_cflags
+daemon_c_flags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON']
 
 sources = files(
   'dhcp/nm-dhcp-client.c',
@@ -49,10 +47,13 @@ sources = files(
 )
 
 deps = [
+  daemon_nm_default_dep,
+  libn_dhcp4_dep,
+  libnm_core_dep,
+  libnm_systemd_shared_dep,
+  libnm_udev_aux_dep,
   libsystemd_dep,
   libudev_dep,
-  libnm_core_dep,
-  shared_n_dhcp4_dep,
 ]
 
 if enable_wext
@@ -63,8 +64,7 @@ libnetwork_manager_base = static_library(
   nm_name + 'Base',
   sources: sources,
   dependencies: deps,
-  c_args: cflags,
-  link_with: libnm_core,
+  c_args: daemon_c_flags,
 )
 
 sources = files(
@@ -151,14 +151,20 @@ sources = files(
 )
 
 nm_deps = [
+  daemon_nm_default_dep,
   dl_dep,
+  libn_acd_dep,
   libndp_dep,
   libudev_dep,
-  libnm_core_dep,
-  shared_n_acd_dep,
   logind_dep,
 ]
 
+nm_links = [
+  libnetwork_manager_base,
+  libnm_systemd_core,
+  libnm_systemd_shared,
+]
+
 if enable_concheck
   nm_deps += libcurl_dep
 endif
@@ -179,19 +185,15 @@ libnetwork_manager = static_library(
   nm_name,
   sources: sources,
   dependencies: nm_deps,
-  c_args: cflags,
-  link_with: [
-    libnetwork_manager_base,
-    libnm_systemd_core,
-    libnm_systemd_shared,
-  ],
+  c_args: daemon_c_flags,
+  link_with: nm_links,
 )
 
 deps = [
+  daemon_nm_default_dep,
   dl_dep,
   libndp_dep,
   libudev_dep,
-  libnm_core_dep,
 ]
 
 name = 'nm-iface-helper'
@@ -200,12 +202,8 @@ executable(
   name,
   name + '.c',
   dependencies: deps,
-  c_args: cflags,
-  link_with: [
-    libnetwork_manager_base,
-    libnm_systemd_core,
-    libnm_systemd_shared,
-  ],
+  c_args: daemon_c_flags,
+  link_with: nm_links,
   link_args: ldflags_linker_script_binary,
   link_depends: linker_script_binary,
   install: true,
@@ -213,46 +211,30 @@ executable(
 )
 
 if enable_tests
+  test_c_flags = daemon_c_flags + ['-DNETWORKMANAGER_COMPILATION_TEST']
+  if require_root_tests
+    test_c_flags += ['-DREQUIRE_ROOT_TESTS=1']
+  endif
+
   sources = files(
     'ndisc/nm-fake-ndisc.c',
     'platform/tests/test-common.c',
     'platform/nm-fake-platform.c',
   )
 
-  deps = [
-    libudev_dep,
-    libnm_core_dep,
-  ]
-
-  test_cflags = ['-DNETWORKMANAGER_COMPILATION_TEST']
-  if require_root_tests
-    test_cflags += ['-DREQUIRE_ROOT_TESTS=1']
-  endif
-
   libnetwork_manager_test = static_library(
     nm_name + 'Test',
     sources: sources,
-    dependencies: deps,
-    c_args: cflags + test_cflags,
+    dependencies: daemon_nm_default_dep,
+    c_args: test_c_flags,
     link_with: libnetwork_manager,
   )
 
-  test_nm_dep = declare_dependency(
-    dependencies: nm_dep,
-    compile_args: test_cflags,
+  libnetwork_manager_test_dep = declare_dependency(
+    dependencies: daemon_nm_default_dep,
     link_with: libnetwork_manager_test,
   )
 
-  test_nm_dep_fake = declare_dependency(
-    dependencies: test_nm_dep,
-    compile_args: ['-DSETUP=nm_fake_platform_setup']
-  )
-
-  test_nm_dep_linux = declare_dependency(
-    dependencies: test_nm_dep,
-    compile_args: ['-DSETUP=nm_linux_platform_setup']
-  )
-
   subdir('dnsmasq/tests')
   subdir('ndisc/tests')
   subdir('platform/tests')
@@ -272,7 +254,7 @@ subdir('settings/plugins')
 
 # NetworkManager binary
 
-create_exports_networkmanager = join_paths(meson.source_root(), 'tools', 'create-exports-NetworkManager.sh')
+create_exports_networkmanager = join_paths(source_root, 'tools', 'create-exports-NetworkManager.sh')
 symbol_map_name = 'NetworkManager.ver'
 
 # libNetworkManager.a, as built by meson doesn't contain all symbols
@@ -283,29 +265,30 @@ symbol_map_name = 'NetworkManager.ver'
 network_manager_sym = executable(
   'nm-full-symbols',
   'main.c',
-  c_args: nm_cflags,
-  link_args: '-Wl,--no-gc-sections',
   dependencies: nm_deps,
+  c_args: daemon_c_flags,
+  link_args: '-Wl,--no-gc-sections',
   link_whole: [libnetwork_manager, libnetwork_manager_base, libnm_core],
-  install: false,
 )
 
 # this uses symbols from nm-full-symbols instead of libNetworkManager.a
 ver_script = custom_target(
   symbol_map_name,
-  input: meson.source_root(),
   output: symbol_map_name,
-  depends: [ network_manager_sym, core_plugins ],
-  command: [create_exports_networkmanager, '--called-from-build', '@INPUT@'],
+  depends: [network_manager_sym, core_plugins],
+  command: [create_exports_networkmanager, '--called-from-build', source_root],
 )
 
-ldflags = ['-rdynamic', '-Wl,--version-script,@0@'.format(ver_script.full_path())]
+ldflags = [
+  '-rdynamic',
+  '-Wl,--version-script,@0@'.format(ver_script.full_path()),
+]
 
 network_manager = executable(
   nm_name,
   'main.c',
   dependencies: nm_deps,
-  c_args: nm_cflags,
+  c_args: daemon_c_flags,
   link_with: libnetwork_manager,
   link_args: ldflags,
   link_depends: ver_script,
@@ -314,16 +297,20 @@ network_manager = executable(
 )
 
 if enable_tests
-  foreach plugin : core_plugins
-    test ('sym/' + plugin.full_path().split('/')[-1],
-          network_manager,
-          args: '--version',
-          env: ['LD_BIND_NOW=1', 'LD_PRELOAD=' + plugin.full_path()])
+  foreach plugin: core_plugins
+    plugin_path = plugin.full_path()
+
+    test(
+      'sym/' + plugin_path.split('/')[-1],
+      network_manager,
+      args: '--version',
+      env: ['LD_BIND_NOW=1', 'LD_PRELOAD=' + plugin_path],
+    )
   endforeach
 endif
 
 test(
   'check-config-options',
-  find_program(join_paths(meson.source_root(), 'tools', 'check-config-options.sh')),
-  args: [meson.source_root()]
+  find_program(join_paths(source_root, 'tools', 'check-config-options.sh')),
+  args: source_root,
 )
diff --git a/src/ndisc/nm-fake-ndisc.c b/src/ndisc/nm-fake-ndisc.c
index dadd1743..020764d3 100644
--- a/src/ndisc/nm-fake-ndisc.c
+++ b/src/ndisc/nm-fake-ndisc.c
@@ -1,19 +1,5 @@
-/* nm-fake-ndisc.c - Fake implementation of neighbor discovery
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/ndisc/nm-fake-ndisc.h b/src/ndisc/nm-fake-ndisc.h
index 5cf65bc1..39b88184 100644
--- a/src/ndisc/nm-fake-ndisc.h
+++ b/src/ndisc/nm-fake-ndisc.h
@@ -1,19 +1,5 @@
-/* nm-fake-ndisc.h - Fake implementation of neighbor discovery
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c
index 9352a753..8077099f 100644
--- a/src/ndisc/nm-lndp-ndisc.c
+++ b/src/ndisc/nm-lndp-ndisc.c
@@ -1,19 +1,5 @@
-/* nm-lndp-ndisc.c - Router discovery implementation using libndp
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/ndisc/nm-lndp-ndisc.h b/src/ndisc/nm-lndp-ndisc.h
index f95d5d0c..22f21e41 100644
--- a/src/ndisc/nm-lndp-ndisc.h
+++ b/src/ndisc/nm-lndp-ndisc.h
@@ -1,19 +1,5 @@
-/* nm-lndp-ndisc.h - Implementation of neighbor discovery using libndp
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/ndisc/nm-ndisc-private.h b/src/ndisc/nm-ndisc-private.h
index 450def27..d31ab31f 100644
--- a/src/ndisc/nm-ndisc-private.h
+++ b/src/ndisc/nm-ndisc-private.h
@@ -1,20 +1,6 @@
-/* nm-ndisc.h - Perform IPv6 neighbor discovery
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_NDISC_PRIVATE_H__
diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c
index a8fabb56..41201e24 100644
--- a/src/ndisc/nm-ndisc.c
+++ b/src/ndisc/nm-ndisc.c
@@ -1,19 +1,5 @@
-/* nm-ndisc.c - Perform IPv6 neighbor discovery
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h
index 8e07ca41..92295ac6 100644
--- a/src/ndisc/nm-ndisc.h
+++ b/src/ndisc/nm-ndisc.h
@@ -1,19 +1,5 @@
-/* nm-ndisc.h - Perform IPv6 neighbor discovery
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/ndisc/tests/meson.build b/src/ndisc/tests/meson.build
index 99cf664e..c81e24ad 100644
--- a/src/ndisc/tests/meson.build
+++ b/src/ndisc/tests/meson.build
@@ -3,7 +3,8 @@ test_unit = 'test-ndisc-fake'
 exe = executable(
   test_unit,
   test_unit + '.c',
-  dependencies: test_nm_dep_fake,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
 )
 
 test(
@@ -18,5 +19,6 @@ test = 'test-ndisc-linux'
 exe = executable(
   test,
   test + '.c',
-  dependencies: test_nm_dep_linux,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
 )
diff --git a/src/ndisc/tests/test-ndisc-fake.c b/src/ndisc/tests/test-ndisc-fake.c
index f752376d..8bdc053d 100644
--- a/src/ndisc/tests/test-ndisc-fake.c
+++ b/src/ndisc/tests/test-ndisc-fake.c
@@ -1,19 +1,5 @@
-/* ndisc.c - test program
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/ndisc/tests/test-ndisc-linux.c b/src/ndisc/tests/test-ndisc-linux.c
index 715d4037..e9ceaa2b 100644
--- a/src/ndisc/tests/test-ndisc-linux.c
+++ b/src/ndisc/tests/test-ndisc-linux.c
@@ -1,19 +1,5 @@
-/* ndisc.c - test program
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/nm-act-request.c b/src/nm-act-request.c
index 763b1503..ef36a40d 100644
--- a/src/nm-act-request.c
+++ b/src/nm-act-request.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2012 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
diff --git a/src/nm-act-request.h b/src/nm-act-request.h
index 055b2591..07fd5731 100644
--- a/src/nm-act-request.h
+++ b/src/nm-act-request.h
@@ -1,20 +1,6 @@
-/* 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.
- *
- * (C) Copyright 2005 - 2012 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2005 - 2012 Red Hat, Inc.
  */
 
 #ifndef __NM_ACT_REQUEST_H__
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index b937f732..bf50e55d 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2014 Red Hat, Inc.
  */
 
@@ -721,7 +707,7 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device)
 		                  G_CALLBACK (device_metered_changed), self);
 
 		if (priv->activation_type != NM_ACTIVATION_TYPE_EXTERNAL) {
-			priv->pending_activation_id = g_strdup_printf (NM_PENDING_ACTIONPREFIX_ACTIVATION"%p", (void *)self);
+			priv->pending_activation_id = g_strdup_printf (NM_PENDING_ACTIONPREFIX_ACTIVATION"%"G_GUINT64_FORMAT, priv->version_id);
 			nm_device_add_pending_action (device, priv->pending_activation_id, TRUE);
 		}
 	} else {
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index d9d28378..bf38a1b0 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2012 Red Hat, Inc.
  */
 
diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c
index fe7645f8..b219b35a 100644
--- a/src/nm-audit-manager.c
+++ b/src/nm-audit-manager.c
@@ -1,20 +1,6 @@
-/* NetworkManager audit support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h
index b4ad5db9..d8310eaf 100644
--- a/src/nm-audit-manager.h
+++ b/src/nm-audit-manager.h
@@ -1,20 +1,6 @@
-/* NetworkManager audit support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #ifndef __NM_AUDIT_MANAGER_H__
diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c
index 0663e207..3f248aee 100644
--- a/src/nm-auth-manager.c
+++ b/src/nm-auth-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -56,7 +42,7 @@ typedef struct {
 	guint changed_signal_id;
 	bool disposing:1;
 	bool shutting_down:1;
-	bool polkit_enabled_construct_only:1;
+	NMAuthPolkitMode auth_polkit_mode:3;
 } NMAuthManagerPrivate;
 
 struct _NMAuthManager {
@@ -124,11 +110,6 @@ typedef enum {
 	POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0),
 } PolkitCheckAuthorizationFlags;
 
-typedef enum {
-	IDLE_REASON_AUTHORIZED,
-	IDLE_REASON_NO_DBUS,
-} IdleReason;
-
 struct _NMAuthManagerCallId {
 	CList calls_lst;
 	NMAuthManager *self;
@@ -137,7 +118,7 @@ struct _NMAuthManagerCallId {
 	gpointer user_data;
 	guint64 call_numid;
 	guint idle_id;
-	IdleReason idle_reason:8;
+	bool idle_is_authorized:1;
 };
 
 #define cancellation_id_to_str_a(call_numid) \
@@ -276,25 +257,16 @@ static gboolean
 _call_on_idle (gpointer user_data)
 {
 	NMAuthManagerCallId *call_id = user_data;
-	gs_free_error GError *error = NULL;
-	gboolean is_authorized = FALSE;
+	gboolean is_authorized;
 	gboolean is_challenge = FALSE;
-	const char *error_msg = NULL;
 
+	is_authorized = call_id->idle_is_authorized;
 	call_id->idle_id = 0;
-	if (call_id->idle_reason == IDLE_REASON_AUTHORIZED) {
-		is_authorized = TRUE;
-		_LOG2T (call_id, "completed: authorized=%d, challenge=%d (simulated)",
-		        is_authorized, is_challenge);
-	} else {
-		nm_assert (call_id->idle_reason == IDLE_REASON_NO_DBUS);
-		error_msg = "failure creating GDBusProxy for authorization request";
-		_LOG2T (call_id, "completed: failed due to no D-Bus proxy");
-	}
 
-	if (error_msg)
-		g_set_error_literal (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, error_msg);
-	_call_id_invoke_callback (call_id, is_authorized, is_challenge, error);
+	_LOG2T (call_id, "completed: authorized=%d, challenge=%d (simulated)",
+	        is_authorized, is_challenge);
+
+	_call_id_invoke_callback (call_id, is_authorized, is_challenge, NULL);
 	return G_SOURCE_REMOVE;
 }
 
@@ -342,24 +314,24 @@ nm_auth_manager_check_authorization (NMAuthManager *self,
 
 	call_id = g_slice_new (NMAuthManagerCallId);
 	*call_id = (NMAuthManagerCallId) {
-		.self       = g_object_ref (self),
-		.callback   = callback,
-		.user_data  = user_data,
-		.call_numid = ++priv->call_numid_counter,
+		.self               = g_object_ref (self),
+		.callback           = callback,
+		.user_data          = user_data,
+		.call_numid         = ++priv->call_numid_counter,
+		.idle_is_authorized = TRUE,
 	};
 	c_list_link_tail (&priv->calls_lst_head, &call_id->calls_lst);
 
-	if (!priv->dbus_connection) {
-		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding due to polkit authorization disabled)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		call_id->idle_reason = IDLE_REASON_AUTHORIZED;
-		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
-	} else if (nm_auth_subject_is_internal (subject)) {
+	if (nm_auth_subject_is_internal (subject)) {
 		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for internal request)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		call_id->idle_reason = IDLE_REASON_AUTHORIZED;
 		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
 	} else if (nm_auth_subject_get_unix_process_uid (subject) == 0) {
 		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for root)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		call_id->idle_reason = IDLE_REASON_AUTHORIZED;
+		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
+	} else if (priv->auth_polkit_mode != NM_AUTH_POLKIT_MODE_USE_POLKIT) {
+		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (PolicyKit disabled and always %s authorization to non-root user)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)),
+		        priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ALLOW_ALL ? "grant" : "deny");
+		call_id->idle_is_authorized = (priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ALLOW_ALL);
 		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
 	} else {
 		GVariant *parameters;
@@ -494,11 +466,17 @@ static void
 set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
 	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE ((NMAuthManager *) object);
+	int v_int;
 
 	switch (prop_id) {
 	case PROP_POLKIT_ENABLED:
 		/* construct-only */
-		priv->polkit_enabled_construct_only = !!g_value_get_boolean (value);
+		v_int = g_value_get_int (value);
+		g_return_if_fail (NM_IN_SET (v_int, NM_AUTH_POLKIT_MODE_ROOT_ONLY,
+		                                    NM_AUTH_POLKIT_MODE_ALLOW_ALL,
+		                                    NM_AUTH_POLKIT_MODE_USE_POLKIT));
+		priv->auth_polkit_mode = v_int;
+		nm_assert (priv->auth_polkit_mode == v_int);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -514,6 +492,7 @@ nm_auth_manager_init (NMAuthManager *self)
 	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
 
 	c_list_init (&priv->calls_lst_head);
+	priv->auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
 }
 
 static void
@@ -526,8 +505,11 @@ constructed (GObject *object)
 
 	G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object);
 
-	if (!priv->polkit_enabled_construct_only) {
-		create_message = "polkit disabled";
+	if (priv->auth_polkit_mode != NM_AUTH_POLKIT_MODE_USE_POLKIT) {
+		if (priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ROOT_ONLY)
+			create_message = "polkit disabled, root-only";
+		else
+			create_message = "polkit disabled, allow-all";
 		goto out;
 	}
 
@@ -536,7 +518,8 @@ constructed (GObject *object)
 	if (!priv->dbus_connection) {
 		/* This warrants an info level message. */
 		logl = LOGL_INFO;
-		create_message = "D-Bus connection not available. Polkit is disabled and all requests are authenticated.";
+		create_message = "D-Bus connection not available. Polkit is disabled and only root will be authorized.";
+		priv->auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
 		goto out;
 	}
 
@@ -560,14 +543,17 @@ out:
 }
 
 NMAuthManager *
-nm_auth_manager_setup (gboolean polkit_enabled)
+nm_auth_manager_setup (NMAuthPolkitMode auth_polkit_mode)
 {
 	NMAuthManager *self;
 
 	g_return_val_if_fail (!singleton_instance, singleton_instance);
+	nm_assert (NM_IN_SET (auth_polkit_mode, NM_AUTH_POLKIT_MODE_ROOT_ONLY,
+	                                        NM_AUTH_POLKIT_MODE_ALLOW_ALL,
+	                                        NM_AUTH_POLKIT_MODE_USE_POLKIT));
 
 	self = g_object_new (NM_TYPE_AUTH_MANAGER,
-	                     NM_AUTH_MANAGER_POLKIT_ENABLED, polkit_enabled,
+	                     NM_AUTH_MANAGER_POLKIT_ENABLED, (int) auth_polkit_mode,
 	                     NULL);
 	_LOGD ("set instance");
 
@@ -612,11 +598,11 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass)
 	object_class->dispose = dispose;
 
 	obj_properties[PROP_POLKIT_ENABLED] =
-	     g_param_spec_boolean (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "",
-	                           FALSE,
-	                           G_PARAM_WRITABLE |
-	                           G_PARAM_CONSTRUCT_ONLY |
-	                           G_PARAM_STATIC_STRINGS);
+	     g_param_spec_int (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "",
+	                       NM_AUTH_POLKIT_MODE_ROOT_ONLY, NM_AUTH_POLKIT_MODE_USE_POLKIT, NM_AUTH_POLKIT_MODE_USE_POLKIT,
+	                       G_PARAM_WRITABLE |
+	                       G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/nm-auth-manager.h b/src/nm-auth-manager.h
index 86746d09..33e3bb2c 100644
--- a/src/nm-auth-manager.h
+++ b/src/nm-auth-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -21,6 +7,7 @@
 #define NM_AUTH_MANAGER_H
 
 #include "nm-auth-subject.h"
+#include "nm-config-data.h"
 
 /*****************************************************************************/
 
@@ -63,7 +50,7 @@ typedef struct _NMAuthManagerClass NMAuthManagerClass;
 
 GType nm_auth_manager_get_type (void);
 
-NMAuthManager *nm_auth_manager_setup (gboolean polkit_enabled);
+NMAuthManager *nm_auth_manager_setup (NMAuthPolkitMode auth_polkit_mode);
 NMAuthManager *nm_auth_manager_get (void);
 
 void nm_auth_manager_force_shutdown (NMAuthManager *self);
diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c
index fd39bff1..ecf6b013 100644
--- a/src/nm-auth-subject.c
+++ b/src/nm-auth-subject.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 - 2014 Red Hat, Inc.
  */
 
diff --git a/src/nm-auth-subject.h b/src/nm-auth-subject.h
index 7af76018..7a75aca9 100644
--- a/src/nm-auth-subject.h
+++ b/src/nm-auth-subject.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c
index 7235cba1..a5b951ab 100644
--- a/src/nm-auth-utils.c
+++ b/src/nm-auth-utils.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 Red Hat, Inc.
  */
 
diff --git a/src/nm-auth-utils.h b/src/nm-auth-utils.h
index 5201d260..808d20fd 100644
--- a/src/nm-auth-utils.h
+++ b/src/nm-auth-utils.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 Red Hat, Inc.
  */
 
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c
index 7c50d61f..8cc8e609 100644
--- a/src/nm-checkpoint-manager.c
+++ b/src/nm-checkpoint-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
@@ -223,7 +209,7 @@ nm_checkpoint_manager_destroy (NMCheckpointManager *self,
 	g_return_val_if_fail (path && path[0] == '/', FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	if (nm_streq (path, "/")) {
+	if (!nm_dbus_path_not_empty (path)) {
 		nm_checkpoint_manager_destroy_all (self);
 		return TRUE;
 	}
diff --git a/src/nm-checkpoint-manager.h b/src/nm-checkpoint-manager.h
index 46590a0c..c0549317 100644
--- a/src/nm-checkpoint-manager.h
+++ b/src/nm-checkpoint-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index 9bc979bb..734a40e1 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/nm-checkpoint.h b/src/nm-checkpoint.h
index 708d8dd6..6d27a43f 100644
--- a/src/nm-checkpoint.h
+++ b/src/nm-checkpoint.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 5655c8fd..c787aa98 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Red Hat, Inc.
  * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de>
  */
@@ -95,8 +81,12 @@ typedef struct {
 	int autoconnect_retries_default;
 
 	struct {
+
+		/* from /var/lib/NetworkManager/no-auto-default.state */
 		char **arr;
 		GSList *specs;
+
+		/* from main.no-auto-default setting in NetworkManager.conf. */
 		GSList *specs_config;
 	} no_auto_default;
 
@@ -396,6 +386,61 @@ _nm_config_data_get_keyfile_user (const NMConfigData *self)
 
 /*****************************************************************************/
 
+static NMAuthPolkitMode
+nm_auth_polkit_mode_from_string (const char *str)
+{
+	int as_bool;
+
+	if (!str)
+		return NM_AUTH_POLKIT_MODE_UNKNOWN;
+
+	if (nm_streq (str, "root-only"))
+		return NM_AUTH_POLKIT_MODE_ROOT_ONLY;
+
+	as_bool = _nm_utils_ascii_str_to_bool (str, -1);
+	if (as_bool != -1) {
+		return   as_bool
+		       ? NM_AUTH_POLKIT_MODE_USE_POLKIT
+		       : NM_AUTH_POLKIT_MODE_ALLOW_ALL;
+	}
+
+	return NM_AUTH_POLKIT_MODE_UNKNOWN;
+}
+
+static NMAuthPolkitMode
+_config_data_get_main_auth_polkit (const NMConfigData *self,
+                                   gboolean *out_invalid_config)
+{
+	NMAuthPolkitMode auth_polkit_mode;
+	const char *str;
+
+	str = nm_config_data_get_value (self,
+	                                NM_CONFIG_KEYFILE_GROUP_MAIN,
+	                                NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
+	                                  NM_CONFIG_GET_VALUE_STRIP
+	                                | NM_CONFIG_GET_VALUE_NO_EMPTY);
+	auth_polkit_mode = nm_auth_polkit_mode_from_string (str);
+	if (auth_polkit_mode == NM_AUTH_POLKIT_MODE_UNKNOWN) {
+		NM_SET_OUT (out_invalid_config, (str != NULL));
+		auth_polkit_mode = nm_auth_polkit_mode_from_string (NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT);
+		if (auth_polkit_mode == NM_AUTH_POLKIT_MODE_UNKNOWN) {
+			nm_assert_not_reached ();
+			auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
+		}
+	} else
+		NM_SET_OUT (out_invalid_config, FALSE);
+
+	return auth_polkit_mode;
+}
+
+NMAuthPolkitMode
+nm_config_data_get_main_auth_polkit (const NMConfigData *self)
+{
+	return _config_data_get_main_auth_polkit (self, NULL);
+}
+
+/*****************************************************************************/
+
 /**
  * nm_config_data_get_groups:
  * @self: the #NMConfigData instance
@@ -614,6 +659,7 @@ void
 nm_config_data_log (const NMConfigData *self,
                     const char *prefix,
                     const char *key_prefix,
+                    const char *no_auto_default_file,
                     /* FILE* */ gpointer print_stream)
 {
 	const NMConfigDataPrivate *priv;
@@ -706,6 +752,16 @@ nm_config_data_log (const NMConfigData *self,
 		}
 	}
 
+	_LOG (stream, prefix, "");
+	_LOG (stream, prefix, "# no-auto-default file \"%s\"", no_auto_default_file);
+	{
+		gs_free char *msg = NULL;
+
+		msg = nm_utils_g_slist_strlist_join (priv->no_auto_default.specs, ",");
+		if (msg)
+			_LOG (stream, prefix, "# no-auto-default specs \"%s\"", msg);
+	}
+
 #undef _LOG
 }
 
@@ -1551,6 +1607,26 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data)
 
 /*****************************************************************************/
 
+void
+nm_config_data_get_warnings (const NMConfigData *self,
+                             GPtrArray *warnings)
+{
+	gboolean invalid;
+
+	nm_assert (NM_IS_CONFIG_DATA (self));
+	nm_assert (warnings);
+
+	_config_data_get_main_auth_polkit (self, &invalid);
+	if (invalid) {
+		g_ptr_array_add (warnings,
+		                 g_strdup_printf ("invalid setting for %s.%s (should be one of \"true\", \"false\", \"root-only\")",
+		                                  NM_CONFIG_KEYFILE_GROUP_MAIN,
+		                                  NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT));
+	}
+}
+
+/*****************************************************************************/
+
 static void
 get_property (GObject *object,
               guint prop_id,
@@ -1662,7 +1738,7 @@ set_property (GObject *object,
 				specs = g_slist_prepend (specs, spec);
 			}
 
-			priv->no_auto_default.arr = nm_utils_strv_dup (value_arr, j);
+			priv->no_auto_default.arr = nm_utils_strv_dup (value_arr, j, TRUE);
 			priv->no_auto_default.specs = g_slist_reverse (specs);
 		}
 		break;
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index 8f925a6c..2a3f2a89 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -1,25 +1,33 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef NM_CONFIG_DATA_H
 #define NM_CONFIG_DATA_H
 
+/*****************************************************************************/
+
+typedef enum {
+
+	/* an invalid mode. */
+	NM_AUTH_POLKIT_MODE_UNKNOWN,
+
+	/* don't use PolicyKit, but only allow root user (uid 0). */
+	NM_AUTH_POLKIT_MODE_ROOT_ONLY,
+
+	/* don't use PolicyKit, but allow all requests. */
+	NM_AUTH_POLKIT_MODE_ALLOW_ALL,
+
+	/* use PolicyKit to authorize requests. Root user (uid 0) always
+	 * gets a free pass, without consulting PolicyKit. If PolicyKit is not
+	 * running, authorization will fail for non root users. */
+	NM_AUTH_POLKIT_MODE_USE_POLKIT,
+
+} NMAuthPolkitMode;
+
+/*****************************************************************************/
+
 #define NM_TYPE_CONFIG_DATA            (nm_config_data_get_type ())
 #define NM_CONFIG_DATA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONFIG_DATA, NMConfigData))
 #define NM_CONFIG_DATA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_CONFIG_DATA, NMConfigDataClass))
@@ -38,25 +46,6 @@
 #define NM_CONFIG_DATA_NO_AUTO_DEFAULT       "no-auto-default"
 #define NM_CONFIG_DATA_DNS_MODE              "dns"
 
-/* The flags for Reload. Currently these are internal defines,
- * only their numeric value matters and must be stable as
- * they are public API! Also, the enum must fit in uint32. */
-enum { /*< skip >*/
-	NM_MANAGER_RELOAD_FLAGS_NONE                            = 0,
-
-	/* reload the configuration from disk */
-	NM_MANAGER_RELOAD_FLAGS_CONF                            = (1LL << 0),
-
-	/* write DNS configuration to resolv.conf */
-	NM_MANAGER_RELOAD_FLAGS_DNS_RC                          = (1LL << 1),
-
-	/* restart the DNS plugin (includes DNS_RC) */
-	NM_MANAGER_RELOAD_FLAGS_DNS_FULL                        = (1LL << 2),
-
-	_NM_MANAGER_RELOAD_FLAGS_ALL,
-	NM_MANAGER_RELOAD_FLAGS_ALL = ((_NM_MANAGER_RELOAD_FLAGS_ALL - 1) << 1) - 1,
-};
-
 typedef enum { /*< flags >*/
 	NM_CONFIG_GET_VALUE_NONE                   = 0,
 
@@ -143,9 +132,10 @@ NMConfigData *nm_config_data_new_update_no_auto_default (const NMConfigData *bas
 NMConfigChangeFlags nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data);
 
 void nm_config_data_log (const NMConfigData *self,
-                               const char *prefix,
-                               const char *key_prefix,
-                               /* FILE* */ gpointer print_stream);
+                         const char *prefix,
+                         const char *key_prefix,
+                         const char *no_auto_default_file,
+                         /* FILE* */ gpointer print_stream);
 
 const char *nm_config_data_get_config_main_file (const NMConfigData *config_data);
 const char *nm_config_data_get_config_description (const NMConfigData *config_data);
@@ -164,6 +154,8 @@ const char *nm_config_data_get_connectivity_response (const NMConfigData *config
 
 int nm_config_data_get_autoconnect_retries_default (const NMConfigData *config_data);
 
+NMAuthPolkitMode nm_config_data_get_main_auth_polkit (const NMConfigData *config_data);
+
 const char *const*nm_config_data_get_no_auto_default (const NMConfigData *config_data);
 gboolean          nm_config_data_get_no_auto_default_for_device (const NMConfigData *self, NMDevice *device);
 
@@ -242,6 +234,9 @@ void nm_global_dns_config_free (NMGlobalDnsConfig *dns_config);
 NMGlobalDnsConfig *nm_global_dns_config_from_dbus (const GValue *value, GError **error);
 void nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns_config, GValue *value);
 
+void nm_config_data_get_warnings (const NMConfigData *self,
+                                  GPtrArray *warnings);
+
 /* private accessors */
 GKeyFile *_nm_config_data_get_keyfile (const NMConfigData *self);
 GKeyFile *_nm_config_data_get_keyfile_user (const NMConfigData *self);
diff --git a/src/nm-config.c b/src/nm-config.c
index d1279814..a7bb3503 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Red Hat, Inc.
  * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de>
  */
@@ -344,6 +330,12 @@ nm_config_get_first_start (NMConfig *config)
 	return NM_CONFIG_GET_PRIVATE (config)->cli.first_start;
 }
 
+const char *
+nm_config_get_no_auto_default_file (NMConfig *config)
+{
+	return NM_CONFIG_GET_PRIVATE (config)->no_auto_default_file;
+}
+
 /*****************************************************************************/
 
 static char **
@@ -1218,7 +1210,7 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
                     const char *system_config_dir,
                     char **out_config_main_file,
                     char **out_config_description,
-                    char ***out_warnings,
+                    GPtrArray *warnings,
                     GError **error)
 {
 	gs_unref_keyfile GKeyFile *keyfile = NULL;
@@ -1228,14 +1220,13 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
 	guint i;
 	gs_free char *o_config_main_file = NULL;
 	const char *run_config_dir = "";
-	gs_unref_ptrarray GPtrArray *warnings = NULL;
 
-	g_return_val_if_fail (config_dir, NULL);
-	g_return_val_if_fail (system_config_dir, NULL);
-	g_return_val_if_fail (!out_config_main_file || !*out_config_main_file, FALSE);
-	g_return_val_if_fail (!out_config_description || !*out_config_description, NULL);
-	g_return_val_if_fail (!error || !*error, FALSE);
-	g_return_val_if_fail (out_warnings && !*out_warnings, FALSE);
+	nm_assert (config_dir);
+	nm_assert (system_config_dir);
+	nm_assert (!out_config_main_file || !*out_config_main_file);
+	nm_assert (!out_config_description || !*out_config_description);
+	nm_assert (!error || !*error);
+	nm_assert (warnings);
 
 	if (   (""RUN_CONFIG_DIR)[0] == '/'
 	    && !nm_streq (RUN_CONFIG_DIR, system_config_dir)
@@ -1244,7 +1235,6 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
 
 	/* create a default configuration file. */
 	keyfile = nm_config_create_keyfile ();
-	warnings = g_ptr_array_new_with_free_func (g_free);
 
 	system_confs = _get_config_dir_files (system_config_dir);
 	confs = _get_config_dir_files (config_dir);
@@ -1334,10 +1324,6 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
 	}
 	NM_SET_OUT (out_config_main_file, g_steal_pointer (&o_config_main_file));
 
-	g_ptr_array_add (warnings, NULL);
-	*out_warnings = (char **) g_ptr_array_free (warnings, warnings->len == 1);
-	g_steal_pointer (&warnings);
-
 	return g_steal_pointer (&keyfile);
 }
 
@@ -2580,7 +2566,7 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emi
 	char *config_description = NULL;
 	gs_strfreev char **no_auto_default = NULL;
 	gboolean intern_config_needs_rewrite;
-	gs_strfreev char **warnings = NULL;
+	gs_unref_ptrarray GPtrArray *warnings = NULL;
 	guint i;
 
 	g_return_if_fail (NM_IS_CONFIG (self));
@@ -2597,6 +2583,8 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emi
 		return;
 	}
 
+	warnings = g_ptr_array_new_with_free_func (g_free);
+
 	/* pass on the original command line options. This means, that
 	 * options specified at command line cannot ever be reloaded from
 	 * file. That seems desirable.
@@ -2606,7 +2594,7 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emi
 	                              priv->system_config_dir,
 	                              &config_main_file,
 	                              &config_description,
-	                              &warnings,
+	                              warnings,
 	                              &error);
 	if (!keyfile) {
 		_LOGE ("Failed to reload the configuration: %s", error->message);
@@ -2615,11 +2603,6 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emi
 		return;
 	}
 
-	if (emit_warnings && warnings) {
-		for (i = 0; warnings[i]; i++)
-			_LOGW ("%s", warnings[i]);
-	}
-
 	no_auto_default = no_auto_default_from_file (priv->no_auto_default_file);
 
 	keyfile_intern = intern_config_read (priv->intern_config_file,
@@ -2636,6 +2619,13 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emi
 	                               (const char *const*) no_auto_default,
 	                               keyfile,
 	                               keyfile_intern);
+
+	if (emit_warnings) {
+		nm_config_data_get_warnings (priv->config_data_orig, warnings);
+		for (i = 0; i < warnings->len; i++)
+			_LOGW ("%s", (const char *) warnings->pdata[i]);
+	}
+
 	g_free (config_main_file);
 	g_free (config_description);
 	g_key_file_unref (keyfile);
@@ -2703,7 +2693,7 @@ _set_config_data (NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags re
 		_LOGI ("signal: %s (%s)",
 		       nm_config_change_flags_to_string (changes, NULL, 0),
 		       nm_config_data_get_config_description (new_data));
-		nm_config_data_log (new_data, "CONFIG: ", "  ", NULL);
+		nm_config_data_log (new_data, "CONFIG: ", "  ", priv->no_auto_default_file, NULL);
 		priv->config_data = new_data;
 	} else if (had_new_data)
 		_LOGI ("signal: %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0));
@@ -2787,7 +2777,7 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
 	gs_free char *config_main_file = NULL;
 	gs_free char *config_description = NULL;
 	gs_strfreev char **no_auto_default = NULL;
-	gs_strfreev char **warnings = NULL;
+	gs_unref_ptrarray GPtrArray *warnings = NULL;
 	gs_free char *configure_and_quit = NULL;
 	gboolean intern_config_needs_rewrite;
 	const char *s;
@@ -2814,12 +2804,14 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
 	else
 		priv->intern_config_file = g_strdup (DEFAULT_INTERN_CONFIG_FILE);
 
+	warnings = g_ptr_array_new_with_free_func (g_free);
+
 	keyfile = read_entire_config (&priv->cli,
 	                              priv->config_dir,
 	                              priv->system_config_dir,
 	                              &config_main_file,
 	                              &config_description,
-	                              &warnings,
+	                              warnings,
 	                              error);
 	if (!keyfile)
 		return FALSE;
@@ -2865,8 +2857,13 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
 	                                             keyfile,
 	                                             keyfile_intern);
 
+	nm_config_data_get_warnings (priv->config_data_orig, warnings);
+
 	priv->config_data = g_object_ref (priv->config_data_orig);
-	priv->warnings = g_steal_pointer (&warnings);
+	if (warnings->len > 0) {
+		g_ptr_array_add (warnings, NULL);
+		priv->warnings = (char **) g_ptr_array_free (g_steal_pointer (&warnings), FALSE);
+	}
 	return TRUE;
 }
 
diff --git a/src/nm-config.h b/src/nm-config.h
index 32ce236f..d9460ebb 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Red Hat, Inc.
  * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de>
  */
@@ -163,6 +149,8 @@ gboolean nm_config_get_is_debug (NMConfig *config);
 
 gboolean nm_config_get_first_start (NMConfig *config);
 
+const char *nm_config_get_no_auto_default_file (NMConfig *config);
+
 void nm_config_set_values (NMConfig *self,
                            GKeyFile *keyfile_intern_new,
                            gboolean allow_write,
@@ -231,7 +219,6 @@ extern char *_nm_config_match_env;
 
 #define NM_CONFIG_DEVICE_STATE_DIR ""NMRUNDIR"/devices"
 
-#define NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL     (nm_streq (""NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT, "true"))
 #define NM_CONFIG_DEFAULT_LOGGING_AUDIT_BOOL        (nm_streq (""NM_CONFIG_DEFAULT_LOGGING_AUDIT, "true"))
 
 typedef enum {
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 694733f6..ccac6376 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
  * Copyright (C) 2011 Dan Williams <dcbw@redhat.com>
  * Copyright (C) 2016 - 2018 Red Hat, Inc.
@@ -27,6 +13,7 @@
 #include <curl/curl.h>
 #endif
 #include <linux/rtnetlink.h>
+#include <glib-unix.h>
 
 #include "c-list/src/c-list.h"
 #include "nm-core-internal.h"
@@ -327,7 +314,6 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask)
 {
 	NMConnectivityCheckHandle *cb_data;
 	CURLMsg *msg;
-	CURLcode eret;
 	int m_left;
 	long response_code;
 	CURLMcode ret;
@@ -336,12 +322,13 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask)
 
 	ret = curl_multi_socket_action (mhandle, sockfd, ev_bitmask, &running_handles);
 	if (ret != CURLM_OK) {
-		_LOGD ("connectivity check failed: (%d) %s", ret, curl_easy_strerror (ret));
+		_LOGD ("connectivity check failed: (%d) %s", ret, curl_multi_strerror (ret));
 		success = FALSE;
 	}
 
 	while ((msg = curl_multi_info_read (mhandle, &m_left))) {
 		const char *response;
+		CURLcode eret;
 
 		if (msg->msg != CURLMSG_DONE)
 			continue;
@@ -422,10 +409,9 @@ _con_curl_timeout_cb (gpointer user_data)
 {
 	NMConnectivityCheckHandle *cb_data = user_data;
 
-	cb_data->concheck.curl_timer = 0;
 	_con_curl_check_connectivity (cb_data->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0);
 	_complete_queued (cb_data->self);
-	return G_SOURCE_REMOVE;
+	return G_SOURCE_CONTINUE;
 }
 
 static int
@@ -441,7 +427,8 @@ multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata)
 
 typedef struct {
 	NMConnectivityCheckHandle *cb_data;
-	GIOChannel *ch;
+
+	GSource *source;
 
 	/* this is a very simplistic weak-pointer. If ConCurlSockData gets
 	 * destroyed, it will set *destroy_notify to TRUE.
@@ -450,15 +437,15 @@ typedef struct {
 	 * safely access @fdp after _con_curl_check_connectivity(). */
 	gboolean *destroy_notify;
 
-	guint ev;
 } ConCurlSockData;
 
 static gboolean
-_con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_data)
+_con_curl_socketevent_cb (int fd,
+                          GIOCondition condition,
+                          gpointer user_data)
 {
 	ConCurlSockData *fdp = user_data;
 	NMConnectivityCheckHandle *cb_data = fdp->cb_data;
-	int fd = g_io_channel_unix_get_fd (ch);
 	int action = 0;
 	gboolean fdp_destroyed = FALSE;
 	gboolean success;
@@ -482,12 +469,12 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_
 		nm_assert (fdp->destroy_notify == &fdp_destroyed);
 		fdp->destroy_notify = NULL;
 		if (!success)
-			fdp->ev = 0;
+			nm_clear_g_source_inst (&fdp->source);
 	}
 
 	_complete_queued (cb_data->self);
 
-	return success ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
+	return G_SOURCE_CONTINUE;
 }
 
 static int
@@ -495,7 +482,6 @@ multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, voi
 {
 	NMConnectivityCheckHandle *cb_data = userdata;
 	ConCurlSockData *fdp = socketp;
-	GIOCondition condition = 0;
 
 	(void) _NM_ENSURE_TYPE (int, fd);
 
@@ -503,19 +489,21 @@ multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, voi
 		if (fdp) {
 			if (fdp->destroy_notify)
 				*fdp->destroy_notify = TRUE;
+			nm_clear_g_source_inst (&fdp->source);
 			curl_multi_assign (cb_data->concheck.curl_mhandle, fd, NULL);
-			nm_clear_g_source (&fdp->ev);
-			g_io_channel_unref (fdp->ch);
 			g_slice_free (ConCurlSockData, fdp);
 		}
 	} else {
+		GIOCondition condition;
+
 		if (!fdp) {
-			fdp = g_slice_new0 (ConCurlSockData);
-			fdp->cb_data = cb_data;
-			fdp->ch = g_io_channel_unix_new (fd);
+			fdp = g_slice_new (ConCurlSockData);
+			*fdp = (ConCurlSockData) {
+				.cb_data = cb_data,
+			};
 			curl_multi_assign (cb_data->concheck.curl_mhandle, fd, fdp);
 		} else
-			nm_clear_g_source (&fdp->ev);
+			nm_clear_g_source_inst (&fdp->source);
 
 		if (what == CURL_POLL_IN)
 			condition = G_IO_IN;
@@ -523,9 +511,14 @@ multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, voi
 			condition = G_IO_OUT;
 		else if (what == CURL_POLL_INOUT)
 			condition = G_IO_IN | G_IO_OUT;
+		else
+			condition = 0;
 
-		if (condition)
-			fdp->ev = g_io_add_watch (fdp->ch, condition, _con_curl_socketevent_cb, fdp);
+		if (condition) {
+			fdp->source = g_unix_fd_source_new (fd, condition);
+			g_source_set_callback (fdp->source, G_SOURCE_FUNC (_con_curl_socketevent_cb), fdp, NULL);
+			g_source_attach (fdp->source, NULL);
+		}
 	}
 
 	return CURLM_OK;
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index 00d0e642..db113f74 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
  * Copyright (C) 2017 Red Hat, Inc.
  */
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index d896d4d3..fb92289f 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -1,21 +1,7 @@
-/* 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 2004 - 2018 Red Hat, Inc.
- * Copyright 2005 - 2008 Novell, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2004 - 2018 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
 #include "nm-default.h"
@@ -245,102 +231,6 @@ 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 (dst, NULL);
-
-	switch (family) {
-	case AF_INET:
-		g_return_val_if_fail (plen <= 32, NULL);
-
-		if (!src) {
-			/* allow "self-assignment", by specifying %NULL as source. */
-			src = dst;
-		}
-
-		*((guint32 *) dst) = nm_utils_ip4_address_clear_host_address (*((guint32 *) src), plen);
-		break;
-	case AF_INET6:
-		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
- *
- * returns: the input address, with the host address set to 0.
- */
-in_addr_t
-nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen)
-{
-	return addr & _nm_utils_ip4_prefix_to_netmask (plen);
-}
-
-/* nm_utils_ip6_address_clear_host_address:
- * @dst: destination output buffer, will contain the network part of the @src address
- * @src: source ip6 address
- * @plen: prefix length of network
- *
- * Note: this function is self assignment safe, to update @src inplace, set both
- * @dst and @src to the same destination or set @src NULL.
- */
-const struct in6_addr *
-nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen)
-{
-	g_return_val_if_fail (plen <= 128, NULL);
-	g_return_val_if_fail (dst, NULL);
-
-	if (!src)
-		src = dst;
-
-	if (plen < 128) {
-		guint nbytes = plen / 8;
-		guint nbits = plen % 8;
-
-		if (nbytes && dst != src)
-			memcpy (dst, src, nbytes);
-		if (nbits) {
-			dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
-			nbytes++;
-		}
-		if (nbytes <= 15)
-			memset (&dst->s6_addr[nbytes], 0, 16 - nbytes);
-	} else if (src != dst)
-		*dst = *src;
-
-	return dst;
-}
-
-int
-nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen)
-{
-	int nbytes;
-	guint8 va, vb, m;
-
-	if (plen >= 128)
-		NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, sizeof (struct in6_addr));
-	else {
-		nbytes = plen / 8;
-		if (nbytes)
-			NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, nbytes);
-
-		plen = plen % 8;
-		if (plen != 0) {
-			m = ~((1 << (8 - plen)) - 1);
-			va = ((((const guint8 *) addr_a))[nbytes]) & m;
-			vb = ((((const guint8 *) addr_b))[nbytes]) & m;
-			NM_CMP_DIRECT (va, vb);
-		}
-	}
-	return 0;
-}
-
 /*****************************************************************************/
 
 void
@@ -1113,7 +1003,7 @@ const char *const NM_PATHS_DEFAULT[] = {
 };
 
 const char *
-nm_utils_find_helper(const char *progname, const char *try_first, GError **error)
+nm_utils_find_helper (const char *progname, const char *try_first, GError **error)
 {
 	return nm_utils_file_search_in_paths (progname, try_first, NM_PATHS_DEFAULT, G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, error);
 }
@@ -2431,8 +2321,8 @@ again:
 		 * where our configured SYSCONFDIR is.  Alternatively, it might be in
 		 * LOCALSTATEDIR /lib/dbus/machine-id.
 		 */
-		if (   nm_utils_file_get_contents (-1, "/etc/machine-id", 100*1024, 0, &content, NULL, NULL) >= 0
-		    || nm_utils_file_get_contents (-1, LOCALSTATEDIR"/lib/dbus/machine-id", 100*1024, 0, &content, NULL, NULL) >= 0) {
+		if (   nm_utils_file_get_contents (-1, "/etc/machine-id",                   100*1024, 0, &content, NULL, NULL, NULL)
+		    || nm_utils_file_get_contents (-1, LOCALSTATEDIR"/lib/dbus/machine-id", 100*1024, 0, &content, NULL, NULL, NULL)) {
 			g_strstrip (content);
 			if (nm_utils_hexstr2bin_full (content,
 			                              FALSE,
@@ -2615,13 +2505,14 @@ _host_id_read (guint8 **out_host_id,
 	GError *error = NULL;
 	gboolean success;
 
-	if (nm_utils_file_get_contents (-1,
-	                                SECRET_KEY_FILE,
-	                                10*1024,
-	                                NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
-	                                (char **) &file_content.str,
-	                                &file_content.len,
-	                                &error) < 0) {
+	if (!nm_utils_file_get_contents (-1,
+	                                 SECRET_KEY_FILE,
+	                                 10*1024,
+	                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
+	                                 &file_content.str,
+	                                 &file_content.len,
+	                                 NULL,
+	                                 &error)) {
 		if (!nm_utils_error_is_notfound (error)) {
 			nm_log_warn (LOGD_CORE, "secret-key: failure reading secret key in \"%s\": %s (generate new key)",
 			             SECRET_KEY_FILE, error->message);
@@ -2699,6 +2590,7 @@ _host_id_read (guint8 **out_host_id,
 		                                        (const char *) new_content,
 		                                        len,
 		                                        0600,
+		                                        NULL,
 		                                        &error)) {
 			nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)",
 			             SECRET_KEY_FILE, error->message);
@@ -2809,9 +2701,14 @@ again:
 		NMUuid uuid;
 		gboolean is_fake = FALSE;
 
-		nm_utils_file_get_contents (-1, "/proc/sys/kernel/random/boot_id", 0,
+		nm_utils_file_get_contents (-1,
+		                            "/proc/sys/kernel/random/boot_id",
+		                            0,
 		                            NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-		                            &contents, NULL, NULL);
+		                            &contents,
+		                            NULL,
+		                            NULL,
+		                            NULL);
 		if (   !contents
 		    || !_nm_utils_uuid_parse (nm_strstrip (contents), &uuid)) {
 			/* generate a random UUID instead. */
@@ -3625,12 +3522,7 @@ nm_utils_create_dhcp_iaid (gboolean legacy_unstable_byteorder,
 
 /**
  * nm_utils_dhcp_client_id_systemd_node_specific_full:
- * @legacy_unstable_byteorder: historically, the code would generate a iaid
- *   dependent on host endianness. This is undesirable, if backward compatibility
- *   are not a concern, generate stable endianness.
- * @interface_id: a binary identifier that is hashed into the DUID.
- *   Comonly this is the interface-name, but it may be the MAC address.
- * @interface_id_len: the length of @interface_id.
+ * @iaid: the IAID (identity association identifier) in native byte order
  * @machine_id: the binary identifier for the machine. It is hashed
  *   into the DUID. It commonly is /etc/machine-id (parsed in binary as NMUuid).
  * @machine_id_len: the length of the @machine_id.
@@ -3642,9 +3534,7 @@ nm_utils_create_dhcp_iaid (gboolean legacy_unstable_byteorder,
  * Returns: a %GBytes of generated client-id. This function cannot fail.
  */
 GBytes *
-nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byteorder,
-                                                    const guint8 *interface_id,
-                                                    gsize interface_id_len,
+nm_utils_dhcp_client_id_systemd_node_specific_full (guint32 iaid,
                                                     const guint8 *machine_id,
                                                     gsize machine_id_len)
 {
@@ -3665,24 +3555,15 @@ nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byt
 		} duid;
 	} *client_id;
 	guint64 u64;
-	guint32 u32;
 
-	g_return_val_if_fail (interface_id, NULL);
-	g_return_val_if_fail (interface_id_len > 0, NULL);
 	g_return_val_if_fail (machine_id, NULL);
 	g_return_val_if_fail (machine_id_len > 0, NULL);
 
 	client_id = g_malloc (sizeof (*client_id));
 
 	client_id->type = 255;
-
-	u32 = nm_utils_create_dhcp_iaid (legacy_unstable_byteorder,
-	                                 interface_id,
-	                                 interface_id_len);
-	unaligned_write_be32 (&client_id->iaid, u32);
-
+	unaligned_write_be32 (&client_id->iaid, iaid);
 	unaligned_write_be16 (&client_id->duid.type, DUID_TYPE_EN);
-
 	unaligned_write_be32 (&client_id->duid.en.pen, SYSTEMD_PEN);
 
 	u64 = htole64 (c_siphash_hash (HASH_KEY, machine_id, machine_id_len));
@@ -3693,14 +3574,9 @@ nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byt
 }
 
 GBytes *
-nm_utils_dhcp_client_id_systemd_node_specific (gboolean legacy_unstable_byteorder,
-                                               const char *ifname)
+nm_utils_dhcp_client_id_systemd_node_specific (guint32 iaid)
 {
-	g_return_val_if_fail (ifname && ifname[0], NULL);
-
-	return nm_utils_dhcp_client_id_systemd_node_specific_full (legacy_unstable_byteorder,
-	                                                           (const guint8 *) ifname,
-	                                                           strlen (ifname),
+	return nm_utils_dhcp_client_id_systemd_node_specific_full (iaid,
 	                                                           (const guint8 *) nm_utils_machine_id_bin (),
 	                                                           sizeof (NMUuid));
 }
@@ -3751,66 +3627,6 @@ nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings)
 
 /*****************************************************************************/
 
-static gboolean
-debug_key_matches (const char *key,
-                   const char *token,
-                   guint        length)
-{
-	/* may not call GLib functions: see note in g_parse_debug_string() */
-	for (; length; length--, key++, token++) {
-		char k = (*key   == '_') ? '-' : g_ascii_tolower (*key  );
-		char t = (*token == '_') ? '-' : g_ascii_tolower (*token);
-
-		if (k != t)
-			return FALSE;
-	}
-
-	return *key == '\0';
-}
-
-/**
- * nm_utils_parse_debug_string:
- * @string: the string to parse
- * @keys: the debug keys
- * @nkeys: number of entries in @keys
- *
- * Similar to g_parse_debug_string(), but does not special
- * case "help" or "all".
- *
- * Returns: the flags
- */
-guint
-nm_utils_parse_debug_string (const char *string,
-                             const GDebugKey *keys,
-                             guint nkeys)
-{
-	guint i;
-	guint result = 0;
-	const char *q;
-
-	if (string == NULL)
-		return 0;
-
-	while (*string) {
-		q = strpbrk (string, ":;, \t");
-		if (!q)
-			q = string + strlen (string);
-
-		for (i = 0; i < nkeys; i++) {
-			if (debug_key_matches (keys[i].key, string, q - string))
-				result |= keys[i].value;
-		}
-
-		string = q;
-		if (*string)
-			string++;
-	}
-
-	return result;
-}
-
-/*****************************************************************************/
-
 void
 nm_utils_ifname_cpy (char *dst, const char *name)
 {
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index a0efb3b8..7d63d06a 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -1,21 +1,7 @@
-/* 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 2004 - 2016 Red Hat, Inc.
- * Copyright 2005 - 2008 Novell, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2004 - 2016 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
 #ifndef __NM_CORE_UTILS_H__
@@ -402,14 +388,11 @@ guint32 nm_utils_create_dhcp_iaid (gboolean legacy_unstable_byteorder,
                                    const guint8 *interface_id,
                                    gsize interface_id_len);
 
-GBytes *nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byteorder,
-                                                            const guint8 *interface_id,
-                                                            gsize interface_id_len,
+GBytes *nm_utils_dhcp_client_id_systemd_node_specific_full (guint32 iaid,
                                                             const guint8 *machine_id,
                                                             gsize machine_id_len);
 
-GBytes *nm_utils_dhcp_client_id_systemd_node_specific (gboolean legacy_unstable_byteorder,
-                                                       const char *ifname);
+GBytes *nm_utils_dhcp_client_id_systemd_node_specific (guint32 iaid);
 
 /*****************************************************************************/
 
@@ -439,10 +422,6 @@ void _nm_utils_set_testing (NMUtilsTestFlags flags);
 
 void nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings);
 
-guint nm_utils_parse_debug_string (const char *string,
-                                   const GDebugKey *keys,
-                                   guint nkeys);
-
 void nm_utils_ifname_cpy (char *dst, const char *name);
 
 guint32 nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c
index a14ea120..3f6f8115 100644
--- a/src/nm-dbus-manager.c
+++ b/src/nm-dbus-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2013 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -772,86 +758,6 @@ nm_dbus_manager_get_unix_user (NMDBusManager *self,
 
 /*****************************************************************************/
 
-const char *
-nm_dbus_manager_connection_get_private_name (NMDBusManager *self,
-                                             GDBusConnection *connection)
-{
-	NMDBusManagerPrivate *priv;
-	PrivateServer *s;
-	const char *owner;
-
-	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
-	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
-
-	if (g_dbus_connection_get_unique_name (connection)) {
-		/* Shortcut. The connection is not a private connection. */
-		return NULL;
-	}
-
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) {
-		if ((owner = private_server_get_connection_owner (s, connection)))
-			return owner;
-	}
-	g_return_val_if_reached (NULL);
-}
-
-/**
- * nm_dbus_manager_new_proxy:
- * @self: the #NMDBusManager
- * @connection: the GDBusConnection for which this connection should be created
- * @proxy_type: the type of #GDBusProxy to create
- * @name: any name on the message bus
- * @path: name of the object instance to call methods on
- * @iface: name of the interface to call methods on
- *
- * Creates a new proxy (of type @proxy_type) for a name on a given bus.  Since
- * the process which called the D-Bus method could be coming from a private
- * connection or the system bus connection, different proxies must be created
- * for each case.  This function abstracts that.
- *
- * Returns: a #GDBusProxy capable of calling D-Bus methods of the calling process
- */
-GDBusProxy *
-nm_dbus_manager_new_proxy (NMDBusManager *self,
-                           GDBusConnection *connection,
-                           GType proxy_type,
-                           const char *name,
-                           const char *path,
-                           const char *iface)
-{
-	const char *owner;
-	GDBusProxy *proxy;
-	GError *error = NULL;
-
-	g_return_val_if_fail (g_type_is_a (proxy_type, G_TYPE_DBUS_PROXY), NULL);
-	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
-
-	/* Might be a private connection, for which @name is fake */
-	owner = nm_dbus_manager_connection_get_private_name (self, connection);
-	if (owner) {
-		g_return_val_if_fail (!g_strcmp0 (owner, name), NULL);
-		name = NULL;
-	}
-
-	proxy = g_initable_new (proxy_type, NULL, &error,
-	                        "g-connection", connection,
-	                        "g-flags", (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                                    G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS),
-	                        "g-name", name,
-	                        "g-object-path", path,
-	                        "g-interface-name", iface,
-	                        NULL);
-	if (!proxy) {
-		_LOGW ("could not create proxy for %s on connection %s: %s",
-		       iface, name, error->message);
-		g_error_free (error);
-	}
-	return proxy;
-}
-
-/*****************************************************************************/
-
 static const NMDBusInterfaceInfoExtended *
 _reg_data_get_interface_info (RegistrationData *reg_data)
 {
@@ -1542,7 +1448,7 @@ static const GDBusSignalInfo signal_info_objmgr_interfaces_removed = NM_DEFINE_G
 );
 
 static const GDBusInterfaceInfo interface_info_objmgr = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-	"org.freedesktop.DBus.ObjectManager",
+	DBUS_INTERFACE_OBJECT_MANAGER,
 	.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
 		NM_DEFINE_GDBUS_METHOD_INFO (
 			"GetManagedObjects",
diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h
index c4a99563..a6094938 100644
--- a/src/nm-dbus-manager.h
+++ b/src/nm-dbus-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2008 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -87,9 +73,6 @@ gboolean nm_dbus_manager_ensure_uid (NMDBusManager          *self,
                                      GQuark error_domain,
                                      int error_code);
 
-const char *nm_dbus_manager_connection_get_private_name (NMDBusManager *self,
-                                                         GDBusConnection *connection);
-
 gboolean nm_dbus_manager_get_unix_user (NMDBusManager *self,
                                         const char *sender,
                                         gulong *out_uid);
@@ -105,11 +88,4 @@ void nm_dbus_manager_private_server_register (NMDBusManager *self,
                                               const char *path,
                                               const char *tag);
 
-GDBusProxy *nm_dbus_manager_new_proxy (NMDBusManager *self,
-                                       GDBusConnection *connection,
-                                       GType proxy_type,
-                                       const char *name,
-                                       const char *path,
-                                       const char *iface);
-
 #endif /* __NM_DBUS_MANAGER_H__ */
diff --git a/src/nm-dbus-object.c b/src/nm-dbus-object.c
index abf1aa57..a829417c 100644
--- a/src/nm-dbus-object.c
+++ b/src/nm-dbus-object.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/nm-dbus-object.h b/src/nm-dbus-object.h
index 574107a1..8b36bce8 100644
--- a/src/nm-dbus-object.h
+++ b/src/nm-dbus-object.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_DBUS_OBJECT_H__
diff --git a/src/nm-dbus-utils.c b/src/nm-dbus-utils.c
index fe3eff7a..07fa909d 100644
--- a/src/nm-dbus-utils.c
+++ b/src/nm-dbus-utils.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/nm-dbus-utils.h b/src/nm-dbus-utils.h
index cf372e88..8ae6cb3a 100644
--- a/src/nm-dbus-utils.h
+++ b/src/nm-dbus-utils.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_DBUS_UTILS_H__
diff --git a/src/nm-dcb.c b/src/nm-dcb.c
index d6a9946b..d9ab2f57 100644
--- a/src/nm-dcb.c
+++ b/src/nm-dcb.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/nm-dcb.h b/src/nm-dcb.h
index 425e3e23..8c19d0dd 100644
--- a/src/nm-dcb.h
+++ b/src/nm-dcb.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2013 Red Hat, Inc.
  */
 
diff --git a/src/nm-dhcp4-config.c b/src/nm-dhcp4-config.c
index 8390a654..cdbe2071 100644
--- a/src/nm-dhcp4-config.c
+++ b/src/nm-dhcp4-config.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
diff --git a/src/nm-dhcp4-config.h b/src/nm-dhcp4-config.h
index 9853f4ff..3cad1e82 100644
--- a/src/nm-dhcp4-config.h
+++ b/src/nm-dhcp4-config.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c
index 5bbc20ef..676caa6d 100644
--- a/src/nm-dhcp6-config.c
+++ b/src/nm-dhcp6-config.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
diff --git a/src/nm-dhcp6-config.h b/src/nm-dhcp6-config.h
index ffe7b18d..e5697e04 100644
--- a/src/nm-dhcp6-config.h
+++ b/src/nm-dhcp6-config.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c
index 652b9838..54b0567b 100644
--- a/src/nm-dispatcher.c
+++ b/src/nm-dispatcher.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2018 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h
index 5c6c5805..93272dc4 100644
--- a/src/nm-dispatcher.h
+++ b/src/nm-dispatcher.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2012 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c
index 9b0d49c9..cf6494bd 100644
--- a/src/nm-firewall-manager.c
+++ b/src/nm-firewall-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 - 2015 Red Hat, Inc.
  */
 
@@ -21,9 +7,16 @@
 
 #include "nm-firewall-manager.h"
 
-#include "NetworkManagerUtils.h"
+#include "nm-glib-aux/nm-dbus-aux.h"
 #include "c-list/src/c-list.h"
 
+#include "NetworkManagerUtils.h"
+#include "nm-dbus-manager.h"
+
+#define FIREWALL_DBUS_SERVICE         "org.fedoraproject.FirewallD1"
+#define FIREWALL_DBUS_PATH            "/org/fedoraproject/FirewallD1"
+#define FIREWALL_DBUS_INTERFACE_ZONE  "org.fedoraproject.FirewallD1.zone"
+
 /*****************************************************************************/
 
 enum {
@@ -34,11 +27,16 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	GDBusProxy     *proxy;
-	GCancellable   *proxy_cancellable;
+	GDBusConnection *dbus_connection;
+
+	GCancellable *get_name_owner_cancellable;
+
+	CList pending_calls;
 
-	CList           pending_calls;
-	bool            running;
+	guint name_owner_changed_id;
+
+	bool dbus_inited:1;
+	bool running:1;
 } NMFirewallManagerPrivate;
 
 struct _NMFirewallManager {
@@ -61,27 +59,18 @@ NM_DEFINE_SINGLETON_GETTER (NMFirewallManager, nm_firewall_manager_get, NM_TYPE_
 /*****************************************************************************/
 
 typedef enum {
-	CB_INFO_OPS_ADD = 1,
-	CB_INFO_OPS_CHANGE,
-	CB_INFO_OPS_REMOVE,
-} CBInfoOpsType;
-
-typedef enum {
-	CB_INFO_MODE_IDLE = 1,
-	CB_INFO_MODE_DBUS_WAITING,
-	CB_INFO_MODE_DBUS,
-	CB_INFO_MODE_DBUS_COMPLETED,
-} CBInfoMode;
+	OPS_TYPE_ADD = 1,
+	OPS_TYPE_CHANGE,
+	OPS_TYPE_REMOVE,
+} OpsType;
 
 struct _NMFirewallManagerCallId {
 	CList lst;
+
 	NMFirewallManager *self;
-	CBInfoOpsType ops_type;
-	union {
-		const CBInfoMode mode;
-		CBInfoMode mode_mutable;
-	};
+
 	char *iface;
+
 	NMFirewallManagerAddRemoveCallback callback;
 	gpointer user_data;
 
@@ -94,45 +83,57 @@ struct _NMFirewallManagerCallId {
 			guint id;
 		} idle;
 	};
+
+	OpsType ops_type;
+
+	bool is_idle:1;
 };
-typedef struct _NMFirewallManagerCallId CBInfo;
 
 /*****************************************************************************/
 
 static const char *
-_ops_type_to_string (CBInfoOpsType ops_type)
+_ops_type_to_string (OpsType ops_type)
 {
 	switch (ops_type) {
-	case CB_INFO_OPS_ADD:    return "add";
-	case CB_INFO_OPS_REMOVE: return "remove";
-	case CB_INFO_OPS_CHANGE: return "change";
-	default: g_return_val_if_reached ("unknown");
+	case OPS_TYPE_ADD:    return "add";
+	case OPS_TYPE_REMOVE: return "remove";
+	case OPS_TYPE_CHANGE: return "change";
 	}
+	nm_assert_not_reached ();
+	return NULL;
 }
 
 #define _NMLOG_DOMAIN      LOGD_FIREWALL
 #define _NMLOG_PREFIX_NAME "firewall"
-#define _NMLOG(level, info, ...) \
+#define _NMLOG(level, call_id, ...) \
     G_STMT_START { \
         if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
-            CBInfo *__info = (info); \
-            char __prefix_name[30]; \
-            char __prefix_info[64]; \
+            NMFirewallManagerCallId *_call_id = (call_id); \
+            char _prefix_name[30]; \
+            char _prefix_info[100]; \
             \
             _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s: %s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      (self) != singleton_instance \
                         ? ({ \
-                                g_snprintf (__prefix_name, sizeof (__prefix_name), "%s[%p]", ""_NMLOG_PREFIX_NAME, (self)); \
-                                __prefix_name; \
+                                g_snprintf (_prefix_name, \
+                                            sizeof (_prefix_name), \
+                                            "%s["NM_HASH_OBFUSCATE_PTR_FMT"]", \
+                                            ""_NMLOG_PREFIX_NAME,\
+                                            NM_HASH_OBFUSCATE_PTR (self)); \
+                                _prefix_name; \
                            }) \
                         : _NMLOG_PREFIX_NAME, \
-                     __info \
+                     _call_id \
                         ? ({ \
-                                g_snprintf (__prefix_info, sizeof (__prefix_info), "[%p,%s%s:%s%s%s]: ", __info, \
-                                            _ops_type_to_string (__info->ops_type), __info->mode == CB_INFO_MODE_IDLE ? "*" : "", \
-                                            NM_PRINT_FMT_QUOTE_STRING (__info->iface)); \
-                                __prefix_info; \
+                                g_snprintf (_prefix_info, \
+                                            sizeof (_prefix_info), \
+                                            "["NM_HASH_OBFUSCATE_PTR_FMT",%s%s:%s%s%s]: ", \
+                                            NM_HASH_OBFUSCATE_PTR (_call_id), \
+                                            _ops_type_to_string (_call_id->ops_type), \
+                                            _call_id->is_idle ? "*" : "", \
+                                            NM_PRINT_FMT_QUOTE_STRING (_call_id->iface)); \
+                                _prefix_info; \
                            }) \
                         : "" \
                      _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -141,125 +142,152 @@ _ops_type_to_string (CBInfoOpsType ops_type)
 
 /*****************************************************************************/
 
+static gboolean
+_get_running (NMFirewallManagerPrivate *priv)
+{
+	/* when starting, we need to asynchronously check whether there is
+	 * a name owner. During that time we optimistially assume that the
+	 * service is indeed running. That is the time when we queue the
+	 * requests, and they will be started once the get-name-owner call
+	 * returns. */
+	return    priv->running
+	       || (   priv->dbus_connection
+	           && !priv->dbus_inited);
+}
+
 gboolean
 nm_firewall_manager_get_running (NMFirewallManager *self)
 {
 	g_return_val_if_fail (NM_IS_FIREWALL_MANAGER (self), FALSE);
 
-	return NM_FIREWALL_MANAGER_GET_PRIVATE (self)->running;
+	return _get_running (NM_FIREWALL_MANAGER_GET_PRIVATE (self));
 }
 
 /*****************************************************************************/
 
-static CBInfo *
+static NMFirewallManagerCallId *
 _cb_info_create (NMFirewallManager *self,
-                 CBInfoOpsType ops_type,
+                 OpsType ops_type,
                  const char *iface,
                  const char *zone,
                  NMFirewallManagerAddRemoveCallback callback,
                  gpointer user_data)
 {
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
-	CBInfo *info;
-
-	info = g_slice_new0 (CBInfo);
-	info->self = g_object_ref (self);
-	info->ops_type = ops_type;
-	info->iface = g_strdup (iface);
-	info->callback = callback;
-	info->user_data = user_data;
-
-	if (priv->running || priv->proxy_cancellable) {
-		info->mode_mutable = CB_INFO_MODE_DBUS_WAITING;
-		info->dbus.arg = g_variant_new ("(ss)", zone ?: "", iface);
-	} else
-		info->mode_mutable = CB_INFO_MODE_IDLE;
+	NMFirewallManagerCallId *call_id;
 
-	c_list_link_tail (&priv->pending_calls, &info->lst);
+	call_id = g_slice_new0 (NMFirewallManagerCallId);
 
-	return info;
-}
+	call_id->self = g_object_ref (self);
+	call_id->ops_type = ops_type;
+	call_id->iface = g_strdup (iface);
+	call_id->callback = callback;
+	call_id->user_data = user_data;
 
-static void
-_cb_info_free (CBInfo *info)
-{
-	c_list_unlink_stale (&info->lst);
-	if (info->mode != CB_INFO_MODE_IDLE) {
-		if (info->dbus.arg)
-			g_variant_unref (info->dbus.arg);
-		g_clear_object (&info->dbus.cancellable);
-	}
-	g_free (info->iface);
-	if (info->self)
-		g_object_unref (info->self);
-	g_slice_free (CBInfo, info);
-}
+	if (_get_running (priv)) {
+		call_id->is_idle = FALSE;
+		call_id->dbus.arg = g_variant_new ("(ss)", zone ?: "", iface);
+	} else
+		call_id->is_idle = TRUE;
 
-static void
-_cb_info_callback (CBInfo *info,
-                   GError *error)
-{
-	if (info->callback)
-		info->callback (info->self, info, error, info->user_data);
+	c_list_link_tail (&priv->pending_calls, &call_id->lst);
+
+	return call_id;
 }
 
 static void
-_cb_info_complete_normal (CBInfo *info, GError *error)
+_cb_info_complete (NMFirewallManagerCallId *call_id,
+                   GError *error)
 {
-	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (info->self);
+	c_list_unlink (&call_id->lst);
 
-	nm_assert (c_list_contains (&priv->pending_calls, &info->lst));
+	if (call_id->callback)
+		call_id->callback (call_id->self, call_id, error, call_id->user_data);
 
-	c_list_unlink (&info->lst);
-
-	_cb_info_callback (info, error);
-	_cb_info_free (info);
+	if (call_id->is_idle)
+		nm_clear_g_source (&call_id->idle.id);
+	else {
+		nm_g_variant_unref (call_id->dbus.arg);
+		nm_clear_g_cancellable (&call_id->dbus.cancellable);
+	}
+	g_free (call_id->iface);
+	g_object_unref (call_id->self);
+	nm_g_slice_free (call_id);
 }
 
 static gboolean
-_handle_idle (gpointer user_data)
+_handle_idle_cb (gpointer user_data)
 {
 	NMFirewallManager *self;
-	CBInfo *info = user_data;
+	NMFirewallManagerCallId *call_id = user_data;
+
+	nm_assert (call_id);
+	nm_assert (NM_IS_FIREWALL_MANAGER (call_id->self));
+	nm_assert (call_id->is_idle);
+	nm_assert (c_list_contains (&NM_FIREWALL_MANAGER_GET_PRIVATE (call_id->self)->pending_calls, &call_id->lst));
 
-	nm_assert (info && NM_IS_FIREWALL_MANAGER (info->self));
+	self = call_id->self;
 
-	self = info->self;
+	_LOGD (call_id, "complete: fake success");
 
-	_LOGD (info, "complete: fake success");
+	call_id->idle.id = 0;
 
-	_cb_info_complete_normal (info, NULL);
+	_cb_info_complete (call_id, NULL);
 	return G_SOURCE_REMOVE;
 }
 
+static gboolean
+_handle_idle_start (NMFirewallManager *self,
+                    NMFirewallManagerCallId *call_id)
+{
+	if (!call_id->callback) {
+		/* if the user did not provide a callback and firewalld is not running,
+		 * there is no point in scheduling an idle-request to fake success. Just
+		 * return right away. */
+		_LOGD (call_id, "complete: drop request simulating success");
+		_cb_info_complete (call_id, NULL);
+		return FALSE;
+	}
+	call_id->idle.id = g_idle_add (_handle_idle_cb, call_id);
+	return TRUE;
+}
+
 static void
-_handle_dbus (GObject *proxy, GAsyncResult *result, gpointer user_data)
+_handle_dbus_cb (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
 {
 	NMFirewallManager *self;
-	CBInfo *info = user_data;
+	NMFirewallManagerCallId *call_id;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *ret = NULL;
 
-	if (info->mode != CB_INFO_MODE_DBUS) {
-		_cb_info_free (info);
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+
+	if (   !ret
+	    && nm_utils_error_is_cancelled (error, FALSE))
 		return;
-	}
 
-	self = info->self;
+	call_id = user_data;
 
-	ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, &error);
+	nm_assert (call_id);
+	nm_assert (NM_IS_FIREWALL_MANAGER (call_id->self));
+	nm_assert (!call_id->is_idle);
+	nm_assert (c_list_contains (&NM_FIREWALL_MANAGER_GET_PRIVATE (call_id->self)->pending_calls, &call_id->lst));
+
+	self = call_id->self;
 
 	if (error) {
 		const char *non_error = NULL;
 
 		g_dbus_error_strip_remote_error (error);
 
-		switch (info->ops_type) {
-		case CB_INFO_OPS_ADD:
-		case CB_INFO_OPS_CHANGE:
+		switch (call_id->ops_type) {
+		case OPS_TYPE_ADD:
+		case OPS_TYPE_CHANGE:
 			non_error = "ZONE_ALREADY_SET";
 			break;
-		case CB_INFO_OPS_REMOVE:
+		case OPS_TYPE_REMOVE:
 			non_error = "UNKNOWN_INTERFACE";
 			break;
 		}
@@ -267,118 +295,122 @@ _handle_dbus (GObject *proxy, GAsyncResult *result, gpointer user_data)
 		    && non_error
 		    && g_str_has_prefix (error->message, non_error)
 		    && NM_IN_SET (error->message[strlen (non_error)], '\0', ':')) {
-			_LOGD (info, "complete: request failed with a non-error (%s)", error->message);
+			_LOGD (call_id, "complete: request failed with a non-error (%s)", error->message);
 
 			/* The operation failed with an error reason that we don't want
 			 * to propagate. Instead, signal success. */
 			g_clear_error (&error);
 		} else
-			_LOGW (info, "complete: request failed (%s)", error->message);
+			_LOGW (call_id, "complete: request failed (%s)", error->message);
 	} else
-		_LOGD (info, "complete: success");
+		_LOGD (call_id, "complete: success");
+
+	g_clear_object (&call_id->dbus.cancellable);
 
-	_cb_info_complete_normal (info, error);
+	_cb_info_complete (call_id, error);
 }
 
 static void
 _handle_dbus_start (NMFirewallManager *self,
-                    CBInfo *info)
+                    NMFirewallManagerCallId *call_id)
 {
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
 	const char *dbus_method = NULL;
 	GVariant *arg;
 
-	nm_assert (info);
+	nm_assert (call_id);
 	nm_assert (priv->running);
-	nm_assert (info->mode == CB_INFO_MODE_DBUS_WAITING);
+	nm_assert (!call_id->is_idle);
+	nm_assert (c_list_contains (&priv->pending_calls, &call_id->lst));
 
-	switch (info->ops_type) {
-	case CB_INFO_OPS_ADD:
+	switch (call_id->ops_type) {
+	case OPS_TYPE_ADD:
 		dbus_method = "addInterface";
 		break;
-	case CB_INFO_OPS_CHANGE:
+	case OPS_TYPE_CHANGE:
 		dbus_method = "changeZone";
 		break;
-	case CB_INFO_OPS_REMOVE:
+	case OPS_TYPE_REMOVE:
 		dbus_method = "removeInterface";
 		break;
 	}
 	nm_assert (dbus_method);
 
-	arg = info->dbus.arg;
-	info->dbus.arg = NULL;
+	arg = g_steal_pointer (&call_id->dbus.arg);
 
 	nm_assert (arg && g_variant_is_floating (arg));
 
-	info->mode_mutable = CB_INFO_MODE_DBUS;
-	info->dbus.cancellable = g_cancellable_new ();
-
-	g_dbus_proxy_call (priv->proxy,
-	                   dbus_method,
-	                   arg,
-	                   G_DBUS_CALL_FLAGS_NONE, 10000,
-	                   info->dbus.cancellable,
-	                   _handle_dbus,
-	                   info);
+	nm_assert (!call_id->dbus.cancellable);
+
+	call_id->dbus.cancellable = g_cancellable_new ();
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        FIREWALL_DBUS_SERVICE,
+	                        FIREWALL_DBUS_PATH,
+	                        FIREWALL_DBUS_INTERFACE_ZONE,
+	                        dbus_method,
+	                        arg,
+	                        NULL,
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        10000,
+	                        call_id->dbus.cancellable,
+	                        _handle_dbus_cb,
+	                        call_id);
 }
 
-static NMFirewallManagerCallId
+static NMFirewallManagerCallId *
 _start_request (NMFirewallManager *self,
-                CBInfoOpsType ops_type,
+                OpsType ops_type,
                 const char *iface,
                 const char *zone,
                 NMFirewallManagerAddRemoveCallback callback,
                 gpointer user_data)
 {
 	NMFirewallManagerPrivate *priv;
-	CBInfo *info;
+	NMFirewallManagerCallId *call_id;
 
 	g_return_val_if_fail (NM_IS_FIREWALL_MANAGER (self), NULL);
 	g_return_val_if_fail (iface && *iface, NULL);
 
 	priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
 
-	info = _cb_info_create (self, ops_type, iface, zone, callback, user_data);
+	call_id = _cb_info_create (self, ops_type, iface, zone, callback, user_data);
 
-	_LOGD (info, "firewall zone %s %s:%s%s%s%s",
-	       _ops_type_to_string (info->ops_type),
+	_LOGD (call_id, "firewall zone %s %s:%s%s%s%s",
+	       _ops_type_to_string (call_id->ops_type),
 	       iface,
 	       NM_PRINT_FMT_QUOTED (zone, "\"", zone, "\"", "default"),
-	       info->mode == CB_INFO_MODE_IDLE
+	       call_id->is_idle
 	         ? " (not running, simulate success)"
 	         : (!priv->running
 	              ? " (waiting to initialize)"
 	              : ""));
 
-	if (info->mode == CB_INFO_MODE_DBUS_WAITING) {
+	if (!call_id->is_idle) {
 		if (priv->running)
-			_handle_dbus_start (self, info);
-		if (!info->callback) {
+			_handle_dbus_start (self, call_id);
+		if (!call_id->callback) {
 			/* if the user did not provide a callback, the call_id is useless.
 			 * Especially, the user cannot use the call-id to cancel the request,
 			 * because he cannot know whether the request is still pending.
 			 *
 			 * Hence, returning %NULL doesn't mean that the request could not be started
-			 * (the request will always be started). */
+			 * (this function never fails and always starts a request). */
 			return NULL;
 		}
-	} else if (info->mode == CB_INFO_MODE_IDLE) {
-		if (!info->callback) {
+	} else {
+		if (!_handle_idle_start (self, call_id)) {
 			/* if the user did not provide a callback and firewalld is not running,
 			 * there is no point in scheduling an idle-request to fake success. Just
 			 * return right away. */
-			_LOGD (info, "complete: drop request simulating success");
-			_cb_info_complete_normal (info, NULL);
 			return NULL;
-		} else
-			info->idle.id = g_idle_add (_handle_idle, info);
-	} else
-		nm_assert_not_reached ();
+		}
+	}
 
-	return info;
+	return call_id;
 }
 
-NMFirewallManagerCallId
+NMFirewallManagerCallId *
 nm_firewall_manager_add_or_change_zone (NMFirewallManager *self,
                                         const char *iface,
                                         const char *zone,
@@ -387,14 +419,14 @@ nm_firewall_manager_add_or_change_zone (NMFirewallManager *self,
                                         gpointer user_data)
 {
 	return _start_request (self,
-	                       add ? CB_INFO_OPS_ADD : CB_INFO_OPS_CHANGE,
+	                       add ? OPS_TYPE_ADD : OPS_TYPE_CHANGE,
 	                       iface,
 	                       zone,
 	                       callback,
 	                       user_data);
 }
 
-NMFirewallManagerCallId
+NMFirewallManagerCallId *
 nm_firewall_manager_remove_from_zone (NMFirewallManager *self,
                                       const char *iface,
                                       const char *zone,
@@ -402,7 +434,7 @@ nm_firewall_manager_remove_from_zone (NMFirewallManager *self,
                                       gpointer user_data)
 {
 	return _start_request (self,
-	                       CB_INFO_OPS_REMOVE,
+	                       OPS_TYPE_REMOVE,
 	                       iface,
 	                       zone,
 	                       callback,
@@ -410,162 +442,165 @@ nm_firewall_manager_remove_from_zone (NMFirewallManager *self,
 }
 
 void
-nm_firewall_manager_cancel_call (NMFirewallManagerCallId call)
+nm_firewall_manager_cancel_call (NMFirewallManagerCallId *call_id)
 {
 	NMFirewallManager *self;
 	NMFirewallManagerPrivate *priv;
-	CBInfo *info = call;
 	gs_free_error GError *error = NULL;
 
-	g_return_if_fail (info);
-	g_return_if_fail (NM_IS_FIREWALL_MANAGER (info->self));
+	g_return_if_fail (call_id);
+	g_return_if_fail (NM_IS_FIREWALL_MANAGER (call_id->self));
+	g_return_if_fail (!c_list_is_empty (&call_id->lst));
 
-	self = info->self;
+	self = call_id->self;
 	priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
 
-	nm_assert (c_list_contains (&priv->pending_calls, &info->lst));
-
-	c_list_unlink (&info->lst);
+	nm_assert (c_list_contains (&priv->pending_calls, &call_id->lst));
 
 	nm_utils_error_set_cancelled (&error, FALSE, "NMFirewallManager");
 
-	_LOGD (info, "complete: cancel (%s)", error->message);
-
-	_cb_info_callback (info, error);
+	_LOGD (call_id, "complete: cancel (%s)", error->message);
 
-	if (info->mode == CB_INFO_MODE_DBUS_WAITING)
-		_cb_info_free (info);
-	else if (info->mode == CB_INFO_MODE_IDLE) {
-		g_source_remove (info->idle.id);
-		_cb_info_free (info);
-	} else {
-		info->mode_mutable = CB_INFO_MODE_DBUS_COMPLETED;
-		g_cancellable_cancel (info->dbus.cancellable);
-		g_clear_object (&info->self);
-	}
+	_cb_info_complete (call_id, error);
 }
 
 /*****************************************************************************/
 
-static gboolean
-name_owner_changed (NMFirewallManager *self)
+static void
+name_owner_changed (NMFirewallManager *self,
+                    const char *owner)
 {
+	_nm_unused gs_unref_object NMFirewallManager *self_keep_alive = g_object_ref (self);
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
-	gs_free char *owner = NULL;
+	gboolean was_running;
 	gboolean now_running;
+	gboolean just_initied;
+
+	owner = nm_str_not_empty (owner);
+
+	if (!owner)
+		_LOGT (NULL, "D-Bus name for firewalld has no owner (firewall stopped)");
+	else
+		_LOGT (NULL, "D-Bus name for firewalld has owner %s (firewall started)", owner);
+
+	was_running = _get_running (priv);
+	just_initied = !priv->dbus_inited;
+
+	priv->dbus_inited = TRUE;
+	priv->running = !!owner;
+
+	now_running = _get_running (priv);
+
+	if (just_initied) {
+		NMFirewallManagerCallId *call_id_safe;
+		NMFirewallManagerCallId *call_id;
+
+		/* We kick of the requests that we have pending. Note that this is
+		 * entirely asynchronous and also we don't invoke any callbacks for
+		 * the user.
+		 * Even _handle_idle_start() just schedules an idle handler. That is,
+		 * because we don't want to callback to the user before emitting the
+		 * DISCONNECTED signal below. Also, emitting callbacks means the user
+		 * can call back to modify the list of pending-calls and we'd have
+		 * to handle reentrancy. */
+		c_list_for_each_entry_safe (call_id, call_id_safe, &priv->pending_calls, lst) {
+
+			nm_assert (!call_id->is_idle);
+			nm_assert (call_id->dbus.arg);
+
+			if (priv->running) {
+				_LOGD (call_id, "initalizing: make D-Bus call");
+				_handle_dbus_start (self, call_id);
+			} else {
+				/* we don't want to invoke callbacks to the user right away. That is because
+				 * the user might schedule/cancel more calls, which messes up the order.
+				 *
+				 * Instead, convert the pending calls to idle requests... */
+				nm_clear_pointer (&call_id->dbus.arg, g_variant_unref);
+				call_id->is_idle = TRUE;
+				_LOGD (call_id, "initializing: fake success on idle");
+				_handle_idle_start (self, call_id);
+			}
+		}
+	}
 
-	owner = g_dbus_proxy_get_name_owner (priv->proxy);
-	now_running = !!owner;
-
-	if (now_running == priv->running)
-		return FALSE;
-
-	priv->running = now_running;
-	_LOGD (NULL, "firewall %s", now_running ? "started" : "stopped");
-	return TRUE;
+	if (was_running != now_running)
+		g_signal_emit (self, signals[STATE_CHANGED], 0, FALSE);
 }
 
 static void
-name_owner_changed_cb (GObject    *object,
-                       GParamSpec *pspec,
-                       gpointer    user_data)
+name_owner_changed_cb (GDBusConnection *connection,
+                       const char *sender_name,
+                       const char *object_path,
+                       const char *interface_name,
+                       const char *signal_name,
+                       GVariant *parameters,
+                       gpointer user_data)
 {
 	NMFirewallManager *self = user_data;
+	const char *new_owner;
 
-	nm_assert (NM_IS_FIREWALL_MANAGER (self));
-	nm_assert (G_IS_DBUS_PROXY (object));
-	nm_assert (NM_FIREWALL_MANAGER_GET_PRIVATE (self)->proxy == G_DBUS_PROXY (object));
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
+		return;
 
-	if (name_owner_changed (self))
-		g_signal_emit (self, signals[STATE_CHANGED], 0, FALSE);
+	g_variant_get (parameters,
+	               "(&s&s&s)",
+	               NULL,
+	               NULL,
+	               &new_owner);
+
+	name_owner_changed (self, new_owner);
 }
 
 static void
-_proxy_new_cb (GObject *source_object,
-               GAsyncResult *result,
-               gpointer user_data)
+get_name_owner_cb (const char *name_owner,
+                   GError *error,
+                   gpointer user_data)
 {
 	NMFirewallManager *self;
 	NMFirewallManagerPrivate *priv;
-	GDBusProxy *proxy;
-	gs_free_error GError *error = NULL;
-	CBInfo *info;
-	CList *iter;
 
-	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
-	if (   !proxy
+	if (   !name_owner
 	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
 	self = user_data;
 	priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
-	g_clear_object (&priv->proxy_cancellable);
 
-	if (!proxy) {
-		_LOGW (NULL, "could not connect to system D-Bus (%s)", error->message);
-		return;
-	}
+	g_clear_object (&priv->get_name_owner_cancellable);
 
-	priv->proxy = proxy;
-	g_signal_connect (priv->proxy, "notify::g-name-owner",
-	                  G_CALLBACK (name_owner_changed_cb), self);
-
-	if (!name_owner_changed (self))
-		_LOGD (NULL, "firewall %s", "initialized (not running)");
-
-again:
-	c_list_for_each (iter, &priv->pending_calls) {
-		info = c_list_entry (iter, CBInfo, lst);
-
-		if (info->mode != CB_INFO_MODE_DBUS_WAITING)
-			continue;
-		if (priv->running) {
-			_LOGD (info, "make D-Bus call");
-			_handle_dbus_start (self, info);
-		} else {
-			_LOGD (info, "complete: fake success");
-			c_list_unlink (&info->lst);
-			_cb_info_callback (info, NULL);
-			_cb_info_free (info);
-			goto again;
-		}
-	}
-
-	/* we always emit a state-changed signal, even if the
-	 * "running" property is still false. */
-	g_signal_emit (self, signals[STATE_CHANGED], 0, TRUE);
+	name_owner_changed (self, name_owner);
 }
 
 /*****************************************************************************/
 
 static void
-nm_firewall_manager_init (NMFirewallManager * self)
+nm_firewall_manager_init (NMFirewallManager *self)
 {
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
 
 	c_list_init (&priv->pending_calls);
-}
-
-static void
-constructed (GObject *object)
-{
-	NMFirewallManager *self = (NMFirewallManager *) object;
-	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
 
-	priv->proxy_cancellable = g_cancellable_new ();
+	priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
 
-	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-	                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
-	                          | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                          NULL,
-	                          FIREWALL_DBUS_SERVICE,
-	                          FIREWALL_DBUS_PATH,
-	                          FIREWALL_DBUS_INTERFACE_ZONE,
-	                          priv->proxy_cancellable,
-	                          _proxy_new_cb,
-	                          self);
+	if (!priv->dbus_connection) {
+		_LOGD (NULL, "no D-Bus connection");
+		return;
+	}
 
-	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->constructed (object);
+	priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+	                                                                                      FIREWALL_DBUS_SERVICE,
+	                                                                                      name_owner_changed_cb,
+	                                                                                      self,
+	                                                                                      NULL);
+
+	priv->get_name_owner_cancellable = g_cancellable_new ();
+	nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+	                                        FIREWALL_DBUS_SERVICE,
+	                                        -1,
+	                                        priv->get_name_owner_cancellable,
+	                                        get_name_owner_cb,
+	                                        self);
 }
 
 static void
@@ -578,10 +613,14 @@ dispose (GObject *object)
 	 * we don't expect pending operations at this point. */
 	nm_assert (c_list_is_empty (&priv->pending_calls));
 
-	nm_clear_g_cancellable (&priv->proxy_cancellable);
-	g_clear_object (&priv->proxy);
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->name_owner_changed_id);
+
+	nm_clear_g_cancellable (&priv->get_name_owner_cancellable);
 
 	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->dispose (object);
+
+	g_clear_object (&priv->dbus_connection);
 }
 
 static void
@@ -589,7 +628,6 @@ nm_firewall_manager_class_init (NMFirewallManagerClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 
 	signals[STATE_CHANGED] =
diff --git a/src/nm-firewall-manager.h b/src/nm-firewall-manager.h
index ceca58be..689e6b06 100644
--- a/src/nm-firewall-manager.h
+++ b/src/nm-firewall-manager.h
@@ -1,30 +1,11 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_FIREWALL_MANAGER_H__
 #define __NETWORKMANAGER_FIREWALL_MANAGER_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"
-
 #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))
@@ -34,7 +15,7 @@
 
 #define NM_FIREWALL_MANAGER_STATE_CHANGED "state-changed"
 
-typedef struct _NMFirewallManagerCallId *NMFirewallManagerCallId;
+typedef struct _NMFirewallManagerCallId NMFirewallManagerCallId;
 
 typedef struct _NMFirewallManager NMFirewallManager;
 typedef struct _NMFirewallManagerClass NMFirewallManagerClass;
@@ -46,22 +27,22 @@ NMFirewallManager *nm_firewall_manager_get (void);
 gboolean nm_firewall_manager_get_running (NMFirewallManager *self);
 
 typedef void (*NMFirewallManagerAddRemoveCallback) (NMFirewallManager *self,
-                                                    NMFirewallManagerCallId call_id,
+                                                    NMFirewallManagerCallId *call_id,
                                                     GError *error,
                                                     gpointer user_data);
 
-NMFirewallManagerCallId nm_firewall_manager_add_or_change_zone (NMFirewallManager *mgr,
-                                                                const char *iface,
-                                                                const char *zone,
-                                                                gboolean add,
-                                                                NMFirewallManagerAddRemoveCallback callback,
-                                                                gpointer user_data);
-NMFirewallManagerCallId nm_firewall_manager_remove_from_zone (NMFirewallManager *mgr,
-                                                              const char *iface,
-                                                              const char *zone,
-                                                              NMFirewallManagerAddRemoveCallback callback,
-                                                              gpointer user_data);
-
-void nm_firewall_manager_cancel_call (NMFirewallManagerCallId fw_call);
+NMFirewallManagerCallId *nm_firewall_manager_add_or_change_zone (NMFirewallManager *mgr,
+                                                                 const char *iface,
+                                                                 const char *zone,
+                                                                 gboolean add,
+                                                                 NMFirewallManagerAddRemoveCallback callback,
+                                                                 gpointer user_data);
+NMFirewallManagerCallId *nm_firewall_manager_remove_from_zone (NMFirewallManager *mgr,
+                                                               const char *iface,
+                                                               const char *zone,
+                                                               NMFirewallManagerAddRemoveCallback callback,
+                                                               gpointer user_data);
+
+void nm_firewall_manager_cancel_call (NMFirewallManagerCallId *call_id);
 
 #endif /* __NETWORKMANAGER_FIREWALL_MANAGER_H__ */
diff --git a/src/nm-hostname-manager.c b/src/nm-hostname-manager.c
index 86da0168..f44e169e 100644
--- a/src/nm-hostname-manager.c
+++ b/src/nm-hostname-manager.c
@@ -1,20 +1,6 @@
-/* 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.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/nm-hostname-manager.h b/src/nm-hostname-manager.h
index 84ee7cb2..b826b14b 100644
--- a/src/nm-hostname-manager.h
+++ b/src/nm-hostname-manager.h
@@ -1,25 +1,10 @@
-/* NetworkManager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Søren Sandmann <sandmann@daimi.au.dk>
  * Dan Williams <dcbw@redhat.com>
  * Tambet Ingo <tambet@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2007 - 2011, 2017 Red Hat, Inc.
- * (C) Copyright 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2011, 2017 Red Hat, Inc.
+ * Copyright (C) 2008 Novell, Inc.
  */
 
 #ifndef __NM_HOSTNAME_MANAGER_H__
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index afe3fc5d..bfb7af57 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -40,6 +26,7 @@
 #include "ndisc/nm-ndisc.h"
 #include "ndisc/nm-lndp-ndisc.h"
 #include "nm-utils.h"
+#include "nm-core-internal.h"
 #include "nm-setting-ip6-config.h"
 #include "systemd/nm-sd.h"
 
@@ -111,6 +98,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 	static NMIP4Config *last_config = NULL;
 	NMIP4Config *existing;
 	gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
+	gs_free_error GError *error = NULL;
 
 	g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
 
@@ -136,6 +124,9 @@ dhcp4_state_changed (NMDhcpClient *client,
 		                           NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN))
 			_LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config");
 
+		if (!last_config && !nm_dhcp_client_accept (client, &error))
+			_LOGW (LOGD_DHCP4, "failed to accept lease: %s", error->message);
+
 		nm_platform_ip4_dev_route_blacklist_set (NM_PLATFORM_GET,
 		                                         gl.ifindex,
 		                                         ip4_dev_route_blacklist);
@@ -530,6 +521,7 @@ main (int argc, char *argv[])
 		                                          !!global_opt.dhcp4_hostname,
 		                                          global_opt.dhcp4_hostname,
 		                                          global_opt.dhcp4_fqdn,
+		                                          NM_DHCP_HOSTNAME_FLAGS_FQDN_DEFAULT_IP4,
 		                                          client_id,
 		                                          NM_DHCP_TIMEOUT_DEFAULT,
 		                                          NULL,
@@ -605,9 +597,10 @@ main (int argc, char *argv[])
 
 /*****************************************************************************/
 
-const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = {
+const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6] = {
+	/* For nm-iface-helper there is no option to choose a DHCP plugin.
+	 * It just uses the "internal" one. */
 	&_nm_dhcp_client_factory_internal,
-	&_nm_dhcp_client_factory_nettools,
 };
 
 /*****************************************************************************/
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 6dd08434..cd14fb8f 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -718,12 +704,22 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self,
 		/* The destination network depends on the peer-address. */
 		network = nm_utils_ip4_address_clear_host_address (my_addr->peer_address, my_addr->plen);
 
+		if (my_addr->external)
+			continue;
+
 		if (_ipv4_is_zeronet (network)) {
 			/* Kernel doesn't add device-routes for destinations that
 			 * start with 0.x.y.z. Skip them. */
 			continue;
 		}
 
+		if (   my_addr->plen == 32
+		    && my_addr->address == my_addr->peer_address) {
+			/* Kernel doesn't add device-routes for /32 addresses unless
+			 * they have a peer. */
+			continue;
+		}
+
 		r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL);
 		route = NMP_OBJECT_CAST_IP4_ROUTE (r);
 
@@ -866,8 +862,13 @@ _nm_ip_config_merge_route_attributes (int addr_family,
 	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0);
 	r->table_coerced = nm_platform_route_table_coerce (table ?: (route_table ?: RT_TABLE_MAIN));
 
-	if (addr_family == AF_INET)
+	if (addr_family == AF_INET) {
+		guint8 scope;
+
 		GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS,        r4->tos,           BYTE,     byte, 0);
+		GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_SCOPE,      scope,             BYTE,     byte, RT_SCOPE_NOWHERE);
+		r4->scope_inv = nm_platform_route_scope_inv (scope);
+	}
 
 	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK,         onlink,            BOOLEAN,  boolean, FALSE);
 
@@ -993,8 +994,6 @@ nm_ip4_config_merge_setting (NMIP4Config *self,
 
 		route.plen = nm_ip_route_get_prefix (s_route);
 		nm_assert (route.plen <= 32);
-		if (route.plen == 0)
-			continue;
 
 		nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
 		if (nm_ip_route_get_metric (s_route) == -1)
@@ -1179,8 +1178,17 @@ nm_ip4_config_merge (NMIP4Config *dst,
 	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
-	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &address)
-		_add_address (dst, NMP_OBJECT_UP_CAST (address), NULL);
+	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &address) {
+		if (   NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_EXTERNAL)
+		    && !address->external) {
+			NMPlatformIP4Address a;
+
+			a = *address;
+			a.external = TRUE;
+			_add_address (dst, NULL, &a);
+		} else
+			_add_address (dst, NMP_OBJECT_UP_CAST (address), NULL);
+	}
 
 	/* nameservers */
 	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index fe0a085b..01a42e54 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -1,20 +1,6 @@
-/* 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) 2008–2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_IP4_CONFIG_H__
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 1810d511..12553657 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -494,6 +480,8 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self,
 		gboolean has_peer;
 		int routes_n, routes_i;
 
+		if (my_addr->external)
+			continue;
 		if (NM_FLAGS_HAS (my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE))
 			continue;
 		if (my_addr->plen == 0)
@@ -672,8 +660,6 @@ nm_ip6_config_merge_setting (NMIP6Config *self,
 
 		route.plen = nm_ip_route_get_prefix (s_route);
 		nm_assert (route.plen <= 128);
-		if (route.plen == 0)
-			continue;
 
 		nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
 		if (nm_ip_route_get_metric (s_route) == -1)
@@ -865,8 +851,17 @@ nm_ip6_config_merge (NMIP6Config *dst,
 	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
-	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &address)
-		_add_address (dst, NMP_OBJECT_UP_CAST (address), NULL);
+	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &address) {
+		if (   NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_EXTERNAL)
+		    && !address->external) {
+			NMPlatformIP6Address a;
+
+			a = *address;
+			a.external = TRUE;
+			_add_address (dst, NULL, &a);
+		} else
+			_add_address (dst, NMP_OBJECT_UP_CAST (address), NULL);
+	}
 
 	/* nameservers */
 	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index 6c7b4cf4..a9fa8f14 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -1,20 +1,6 @@
-/* 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) 2008–2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_IP6_CONFIG_H__
diff --git a/src/nm-keep-alive.c b/src/nm-keep-alive.c
index ba080a81..7a382f17 100644
--- a/src/nm-keep-alive.c
+++ b/src/nm-keep-alive.c
@@ -1,22 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * NetworkManager -- Inhibition management
- *
- * 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 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/nm-keep-alive.h b/src/nm-keep-alive.h
index fcee9d6d..6efec5f5 100644
--- a/src/nm-keep-alive.h
+++ b/src/nm-keep-alive.h
@@ -1,22 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * NetworkManager -- Inhibition management
- *
- * 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 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_KEEP_ALIVE_H__
diff --git a/src/nm-logging.c b/src/nm-logging.c
index ea505a81..34dd2797 100644
--- a/src/nm-logging.c
+++ b/src/nm-logging.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2012 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -36,6 +22,7 @@
 #include <systemd/sd-journal.h>
 #endif
 
+#include "nm-glib-aux/nm-logging-base.h"
 #include "nm-glib-aux/nm-time-utils.h"
 #include "nm-errors.h"
 
@@ -99,22 +86,6 @@ typedef struct {
 } LogDesc;
 
 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;
-} LogLevelDesc;
-
-typedef struct {
 	char *logging_domains_to_string;
 } GlobalMain;
 
@@ -172,16 +143,6 @@ NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL] = {
 
 /*****************************************************************************/
 
-static const LogLevelDesc level_desc[_LOGL_N] = {
-	[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,                   },
-};
-
 static const LogDesc domain_desc[] = {
 	{ LOGD_PLATFORM,  "PLATFORM" },
 	{ LOGD_RFKILL,    "RFKILL" },
@@ -285,14 +246,8 @@ match_log_level (const char  *level,
                  NMLogLevel  *out_level,
                  GError     **error)
 {
-	int i;
-
-	for (i = 0; i < G_N_ELEMENTS (level_desc); i++) {
-		if (!g_ascii_strcasecmp (level_desc[i].name, level)) {
-			*out_level = i;
-			return TRUE;
-		}
-	}
+	if (_nm_log_parse_level (level, out_level))
+		return TRUE;
 
 	g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL,
 	             _("Unknown log level '%s'"), level);
diff --git a/src/nm-logging.h b/src/nm-logging.h
index c604f493..54887b0f 100644
--- a/src/nm-logging.h
+++ b/src/nm-logging.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2012 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -84,9 +70,10 @@ LOGD_IP_from_af (int addr_family)
            (domain), \
            (ifname), \
            (con_uuid), \
-           "%s[%p] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+           "%s["NM_HASH_OBFUSCATE_PTR_FMT"] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
            (prefix) ?: "", \
-           self _NM_UTILS_MACRO_REST(__VA_ARGS__))
+           NM_HASH_OBFUSCATE_PTR (self) \
+           _NM_UTILS_MACRO_REST(__VA_ARGS__))
 
 static inline gboolean
 _nm_log_ptr_is_debug (NMLogLevel level)
@@ -190,114 +177,6 @@ gboolean nm_logging_syslog_enabled (void);
 
 /*****************************************************************************/
 
-/* This is the default definition of _NMLOG_ENABLED(). Special implementations
- * might want to undef this and redefine it. */
-#define _NMLOG_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG_DOMAIN)) )
-
-#define _LOGT(...)          _NMLOG (LOGL_TRACE, __VA_ARGS__)
-#define _LOGD(...)          _NMLOG (LOGL_DEBUG, __VA_ARGS__)
-#define _LOGI(...)          _NMLOG (LOGL_INFO , __VA_ARGS__)
-#define _LOGW(...)          _NMLOG (LOGL_WARN , __VA_ARGS__)
-#define _LOGE(...)          _NMLOG (LOGL_ERR  , __VA_ARGS__)
-
-#define _LOGT_ENABLED(...)  _NMLOG_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
-#define _LOGD_ENABLED(...)  _NMLOG_ENABLED (LOGL_DEBUG, ##__VA_ARGS__)
-#define _LOGI_ENABLED(...)  _NMLOG_ENABLED (LOGL_INFO , ##__VA_ARGS__)
-#define _LOGW_ENABLED(...)  _NMLOG_ENABLED (LOGL_WARN , ##__VA_ARGS__)
-#define _LOGE_ENABLED(...)  _NMLOG_ENABLED (LOGL_ERR  , ##__VA_ARGS__)
-
-#define _LOGT_err(errsv, ...) _NMLOG_err (errsv, LOGL_TRACE, __VA_ARGS__)
-#define _LOGD_err(errsv, ...) _NMLOG_err (errsv, LOGL_DEBUG, __VA_ARGS__)
-#define _LOGI_err(errsv, ...) _NMLOG_err (errsv, LOGL_INFO , __VA_ARGS__)
-#define _LOGW_err(errsv, ...) _NMLOG_err (errsv, LOGL_WARN , __VA_ARGS__)
-#define _LOGE_err(errsv, ...) _NMLOG_err (errsv, LOGL_ERR  , __VA_ARGS__)
-
-/* _LOGT() and _LOGt() both log with level TRACE, but the latter is disabled by default,
- * unless building with --with-more-logging. */
-#if NM_MORE_LOGGING
-#define _LOGt_ENABLED(...)    _NMLOG_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
-#define _LOGt(...)            _NMLOG (LOGL_TRACE, __VA_ARGS__)
-#define _LOGt_err(errsv, ...) _NMLOG_err (errsv, LOGL_TRACE, __VA_ARGS__)
-#else
-/* still call the logging macros to get compile time checks, but they will be optimized out. */
-#define _LOGt_ENABLED(...)    ( FALSE && (_NMLOG_ENABLED (LOGL_TRACE, ##__VA_ARGS__)) )
-#define _LOGt(...)            G_STMT_START { if (FALSE) { _NMLOG (LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
-#define _LOGt_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG_err (errsv, LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
-#endif
-
-/*****************************************************************************/
-
-/* Some implementation define a second set of logging macros, for a separate
- * use. As with the _LOGD() macro family above, the exact implementation
- * depends on the file that uses them.
- * Still, it encourages a common pattern to have the common set of macros
- * like _LOG2D(), _LOG2I(), etc. and have _LOG2t() which by default
- * is disabled at compile time. */
-
-#define _NMLOG2_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG2_DOMAIN)) )
-
-#define _LOG2T(...)          _NMLOG2 (LOGL_TRACE, __VA_ARGS__)
-#define _LOG2D(...)          _NMLOG2 (LOGL_DEBUG, __VA_ARGS__)
-#define _LOG2I(...)          _NMLOG2 (LOGL_INFO , __VA_ARGS__)
-#define _LOG2W(...)          _NMLOG2 (LOGL_WARN , __VA_ARGS__)
-#define _LOG2E(...)          _NMLOG2 (LOGL_ERR  , __VA_ARGS__)
-
-#define _LOG2T_ENABLED(...)  _NMLOG2_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
-#define _LOG2D_ENABLED(...)  _NMLOG2_ENABLED (LOGL_DEBUG, ##__VA_ARGS__)
-#define _LOG2I_ENABLED(...)  _NMLOG2_ENABLED (LOGL_INFO , ##__VA_ARGS__)
-#define _LOG2W_ENABLED(...)  _NMLOG2_ENABLED (LOGL_WARN , ##__VA_ARGS__)
-#define _LOG2E_ENABLED(...)  _NMLOG2_ENABLED (LOGL_ERR  , ##__VA_ARGS__)
-
-#define _LOG2T_err(errsv, ...) _NMLOG2_err (errsv, LOGL_TRACE, __VA_ARGS__)
-#define _LOG2D_err(errsv, ...) _NMLOG2_err (errsv, LOGL_DEBUG, __VA_ARGS__)
-#define _LOG2I_err(errsv, ...) _NMLOG2_err (errsv, LOGL_INFO , __VA_ARGS__)
-#define _LOG2W_err(errsv, ...) _NMLOG2_err (errsv, LOGL_WARN , __VA_ARGS__)
-#define _LOG2E_err(errsv, ...) _NMLOG2_err (errsv, LOGL_ERR  , __VA_ARGS__)
-
-#if NM_MORE_LOGGING
-#define _LOG2t_ENABLED(...)    _NMLOG2_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
-#define _LOG2t(...)            _NMLOG2 (LOGL_TRACE, __VA_ARGS__)
-#define _LOG2t_err(errsv, ...) _NMLOG2_err (errsv, LOGL_TRACE, __VA_ARGS__)
-#else
-/* still call the logging macros to get compile time checks, but they will be optimized out. */
-#define _LOG2t_ENABLED(...)    ( FALSE && (_NMLOG2_ENABLED (LOGL_TRACE, ##__VA_ARGS__)) )
-#define _LOG2t(...)            G_STMT_START { if (FALSE) { _NMLOG2 (LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
-#define _LOG2t_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG2_err (errsv, LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _NMLOG3_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG3_DOMAIN)) )
-
-#define _LOG3T(...)          _NMLOG3 (LOGL_TRACE, __VA_ARGS__)
-#define _LOG3D(...)          _NMLOG3 (LOGL_DEBUG, __VA_ARGS__)
-#define _LOG3I(...)          _NMLOG3 (LOGL_INFO , __VA_ARGS__)
-#define _LOG3W(...)          _NMLOG3 (LOGL_WARN , __VA_ARGS__)
-#define _LOG3E(...)          _NMLOG3 (LOGL_ERR  , __VA_ARGS__)
-
-#define _LOG3T_ENABLED(...)  _NMLOG3_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
-#define _LOG3D_ENABLED(...)  _NMLOG3_ENABLED (LOGL_DEBUG, ##__VA_ARGS__)
-#define _LOG3I_ENABLED(...)  _NMLOG3_ENABLED (LOGL_INFO , ##__VA_ARGS__)
-#define _LOG3W_ENABLED(...)  _NMLOG3_ENABLED (LOGL_WARN , ##__VA_ARGS__)
-#define _LOG3E_ENABLED(...)  _NMLOG3_ENABLED (LOGL_ERR  , ##__VA_ARGS__)
-
-#define _LOG3T_err(errsv, ...) _NMLOG3_err (errsv, LOGL_TRACE, __VA_ARGS__)
-#define _LOG3D_err(errsv, ...) _NMLOG3_err (errsv, LOGL_DEBUG, __VA_ARGS__)
-#define _LOG3I_err(errsv, ...) _NMLOG3_err (errsv, LOGL_INFO , __VA_ARGS__)
-#define _LOG3W_err(errsv, ...) _NMLOG3_err (errsv, LOGL_WARN , __VA_ARGS__)
-#define _LOG3E_err(errsv, ...) _NMLOG3_err (errsv, LOGL_ERR  , __VA_ARGS__)
-
-#if NM_MORE_LOGGING
-#define _LOG3t_ENABLED(...)    _NMLOG3_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
-#define _LOG3t(...)            _NMLOG3 (LOGL_TRACE, __VA_ARGS__)
-#define _LOG3t_err(errsv, ...) _NMLOG3_err (errsv, LOGL_TRACE, __VA_ARGS__)
-#else
-/* still call the logging macros to get compile time checks, but they will be optimized out. */
-#define _LOG3t_ENABLED(...)    ( FALSE && (_NMLOG3_ENABLED (LOGL_TRACE, ##__VA_ARGS__)) )
-#define _LOG3t(...)            G_STMT_START { if (FALSE) { _NMLOG3 (LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
-#define _LOG3t_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG3_err (errsv, LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
-#endif
-
-/*****************************************************************************/
-
 #define __NMLOG_DEFAULT(level, domain, prefix, ...) \
 	G_STMT_START { \
 		nm_log ((level), (domain), NULL, NULL, \
diff --git a/src/nm-manager.c b/src/nm-manager.c
index d112fcf7..132cf5a0 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2009 Novell, Inc.
  * Copyright (C) 2007 - 2017 Red Hat, Inc.
  */
@@ -25,6 +11,10 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sendfile.h>
+#include <limits.h>
 
 #include "nm-glib-aux/nm-c-list.h"
 
@@ -1168,16 +1158,16 @@ _reload_auth_cb (NMAuthChain *chain,
 		                                 NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                                 "Not authorized to reload configuration");
 	} else {
-		if (NM_FLAGS_ANY (flags, ~NM_MANAGER_RELOAD_FLAGS_ALL)) {
+		if (NM_FLAGS_ANY (flags, ~NM_MANAGER_RELOAD_FLAG_ALL)) {
 			/* invalid flags */
 		} else if (flags == 0)
 			reload_type = NM_CONFIG_CHANGE_CAUSE_SIGHUP;
 		else {
-			if (NM_FLAGS_HAS (flags, NM_MANAGER_RELOAD_FLAGS_CONF))
+			if (NM_FLAGS_HAS (flags, NM_MANAGER_RELOAD_FLAG_CONF))
 				reload_type |= NM_CONFIG_CHANGE_CAUSE_CONF;
-			if (NM_FLAGS_HAS (flags, NM_MANAGER_RELOAD_FLAGS_DNS_RC))
+			if (NM_FLAGS_HAS (flags, NM_MANAGER_RELOAD_FLAG_DNS_RC))
 				reload_type |= NM_CONFIG_CHANGE_CAUSE_DNS_RC;
-			if (NM_FLAGS_HAS (flags, NM_MANAGER_RELOAD_FLAGS_DNS_FULL))
+			if (NM_FLAGS_HAS (flags, NM_MANAGER_RELOAD_FLAG_DNS_FULL))
 				reload_type |= NM_CONFIG_CHANGE_CAUSE_DNS_FULL;
 		}
 
@@ -1729,7 +1719,10 @@ nm_manager_get_state (NMManager *manager)
 /*****************************************************************************/
 
 static NMDevice *
-find_parent_device_for_connection (NMManager *self, NMConnection *connection, NMDeviceFactory *cached_factory)
+find_parent_device_for_connection (NMManager *self,
+                                   NMConnection *connection,
+                                   NMDeviceFactory *cached_factory,
+                                   const char **out_parent_spec)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDeviceFactory *factory;
@@ -1739,6 +1732,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 	NMDevice *candidate;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	NM_SET_OUT (out_parent_spec, NULL);
 
 	if (!cached_factory) {
 		factory = nm_device_factory_manager_find_factory_for_connection (connection);
@@ -1751,6 +1745,8 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 	if (!parent_name)
 		return NULL;
 
+	NM_SET_OUT (out_parent_spec, parent_name);
+
 	/* Try as an interface name of a parent device */
 	parent = find_device_by_iface (self, parent_name, NULL, NULL);
 	if (parent)
@@ -1792,6 +1788,9 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
  * @self: the #NMManager
  * @connection: the #NMConnection to get the interface for
  * @out_parent: on success, the parent device if any
+ * @out_parent_spec: on return, a string specifying the parent device
+ *   in the connection. This can be a device name, a MAC address or a
+ *   connection UUID.
  * @error: an error if determining the virtual interface name failed
  *
  * Given @connection, returns the interface name that the connection
@@ -1805,14 +1804,15 @@ char *
 nm_manager_get_connection_iface (NMManager *self,
                                  NMConnection *connection,
                                  NMDevice **out_parent,
+                                 const char **out_parent_spec,
                                  GError **error)
 {
 	NMDeviceFactory *factory;
 	char *iface = NULL;
 	NMDevice *parent = NULL;
 
-	if (out_parent)
-		*out_parent = NULL;
+	NM_SET_OUT (out_parent, NULL);
+	NM_SET_OUT (out_parent_spec, NULL);
 
 	factory = nm_device_factory_manager_find_factory_for_connection (connection);
 	if (!factory) {
@@ -1835,7 +1835,7 @@ nm_manager_get_connection_iface (NMManager *self,
 		goto return_ifname_fom_connection;
 	}
 
-	parent = find_parent_device_for_connection (self, connection, factory);
+	parent = find_parent_device_for_connection (self, connection, factory, out_parent_spec);
 	iface = nm_device_factory_get_connection_iface (factory,
 	                                                connection,
 	                                                parent ? nm_device_get_ip_iface (parent) : NULL,
@@ -1932,6 +1932,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	gs_free NMSettingsConnection **connections = NULL;
 	guint i;
 	gs_free char *iface = NULL;
+	const char *parent_spec;
 	NMDevice *device = NULL, *parent = NULL;
 	NMDevice *dev_candidate;
 	GError *error = NULL;
@@ -1940,7 +1941,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 
-	iface = nm_manager_get_connection_iface (self, connection, &parent, &error);
+	iface = nm_manager_get_connection_iface (self, connection, &parent, &parent_spec, &error);
 	if (!iface) {
 		_LOG3D (LOGD_DEVICE, connection, "can't get a name of a virtual device: %s",
 		        error->message);
@@ -1948,6 +1949,11 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 		return NULL;
 	}
 
+	if (parent_spec && !parent) {
+		/* parent is not ready, wait */
+		return NULL;
+	}
+
 	/* See if there's a device that is already compatible with this connection */
 	c_list_for_each_entry (dev_candidate, &priv->devices_lst_head, devices_lst) {
 		if (nm_device_check_connection_compatible (dev_candidate, connection, NULL)) {
@@ -2018,7 +2024,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 
 		s_con = nm_connection_get_setting_connection (candidate);
 		g_assert (s_con);
-		if (!nm_setting_connection_get_autoconnect (s_con))
+		if (   !nm_setting_connection_get_autoconnect (s_con)
+		    || nm_settings_connection_autoconnect_is_blocked (connections[i]))
 			continue;
 
 		/* Create any backing resources the device needs */
@@ -2061,10 +2068,10 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 		gs_free char *ifname = NULL;
 		NMDevice *parent;
 
-		parent = find_parent_device_for_connection (self, connection, NULL);
+		parent = find_parent_device_for_connection (self, connection, NULL, NULL);
 		if (parent == device) {
 			/* Only try to activate devices that don't already exist */
-			ifname = nm_manager_get_connection_iface (self, connection, &parent, &error);
+			ifname = nm_manager_get_connection_iface (self, connection, &parent, NULL, &error);
 			if (ifname) {
 				if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname))
 					connection_changed (self, sett_conn);
@@ -2133,7 +2140,7 @@ connection_changed_on_idle (NMManager *self,
 	if (priv->connection_changed_on_idle_id == 0)
 		priv->connection_changed_on_idle_id = g_idle_add (connection_changed_on_idle_cb, self);
 
-	if (!nm_c_list_elem_find_first (&priv->connection_changed_on_idle_lst, sett_conn)) {
+	if (!nm_c_list_elem_find_first_ptr (&priv->connection_changed_on_idle_lst, sett_conn)) {
 		c_list_link_tail (&priv->connection_changed_on_idle_lst,
 		                  &nm_c_list_elem_new_stale (g_object_ref (sett_conn))->lst);
 	}
@@ -2690,6 +2697,34 @@ get_existing_connection (NMManager *self,
 }
 
 static gboolean
+copy_lease (const char *src, const char *dst)
+{
+	nm_auto_close int src_fd = -1;
+	int dst_fd;
+	ssize_t res, size = SSIZE_MAX;
+
+	src_fd = open (src, O_RDONLY|O_CLOEXEC);
+	if (src_fd < 0)
+		return FALSE;
+
+	dst_fd = open (dst, O_CREAT|O_EXCL|O_CLOEXEC|O_WRONLY, 0644);
+	if (dst_fd < 0)
+		return FALSE;
+
+	while ((res = sendfile (dst_fd, src_fd, NULL, size)) > 0)
+		size -= res;
+
+	nm_close (dst_fd);
+
+	if (res != 0) {
+		unlink (dst);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
 recheck_assume_connection (NMManager *self,
                            NMDevice *device)
 {
@@ -2730,7 +2765,8 @@ recheck_assume_connection (NMManager *self,
 		                                                  nm_settings_connection_get_uuid (sett_conn),
 		                                                  nm_device_get_iface (device));
 
-		if (rename (initramfs_lease, connection_lease) == 0) {
+		if (copy_lease (initramfs_lease, connection_lease)) {
+			unlink (initramfs_lease);
 			/*
 			 * We've managed to steal the lease used by initramfs before it
 			 * killed off the dhclient. We need to take ownership of the configured
@@ -3207,22 +3243,6 @@ factory_device_added_cb (NMDeviceFactory *factory,
 	}
 }
 
-static gboolean
-factory_component_added_cb (NMDeviceFactory *factory,
-                            GObject *component,
-                            gpointer user_data)
-{
-	NMManager *self = user_data;
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
-
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_notify_component_added (device, component))
-			return TRUE;
-	}
-	return FALSE;
-}
-
 static void
 _register_device_factory (NMDeviceFactory *factory, gpointer user_data)
 {
@@ -3232,10 +3252,18 @@ _register_device_factory (NMDeviceFactory *factory, gpointer user_data)
 	                  NM_DEVICE_FACTORY_DEVICE_ADDED,
 	                  G_CALLBACK (factory_device_added_cb),
 	                  self);
-	g_signal_connect (factory,
-	                  NM_DEVICE_FACTORY_COMPONENT_ADDED,
-	                  G_CALLBACK (factory_component_added_cb),
-	                  self);
+}
+
+/*****************************************************************************/
+
+void
+nm_manager_notify_device_availibility_maybe_changed (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
+
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
+		nm_device_notify_availability_maybe_changed (device);
 }
 
 /*****************************************************************************/
@@ -4574,6 +4602,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	NMAuthSubject *subject;
 	GError *local = NULL;
 	NMConnectionMultiConnect multi_connect;
+	const char *parent_spec;
 
 	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
 	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE);
@@ -4626,7 +4655,14 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 
 		parent = find_parent_device_for_connection (self,
 		                                            nm_settings_connection_get_connection (sett_conn),
-		                                            NULL);
+		                                            NULL,
+		                                            &parent_spec);
+
+		if (parent_spec && !parent) {
+			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
+			             "parent device '%s' not found", parent_spec);
+			return FALSE;
+		}
 
 		if (parent && !nm_device_is_real (parent)) {
 			NMSettingsConnection *parent_con;
@@ -4638,6 +4674,15 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 				return FALSE;
 			}
 
+			if (   nm_active_connection_get_activation_reason (active) == NM_ACTIVATION_REASON_AUTOCONNECT
+			    && nm_settings_connection_autoconnect_blocked_reason_get (parent_con,
+			                                                              NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST)) {
+				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
+				             "the parent connection of %s cannot autoactivate because it is blocked due to user request",
+				             nm_device_get_iface (device));
+				return FALSE;
+			}
+
 			parent_ac = nm_manager_activate_connection (self,
 			                                            parent_con,
 			                                            NULL,
@@ -5181,7 +5226,7 @@ validate_activation_request (NMManager *self,
 			}
 
 			/* Look for an existing device with the connection's interface name */
-			iface = nm_manager_get_connection_iface (self, connection, NULL, error);
+			iface = nm_manager_get_connection_iface (self, connection, NULL, NULL, error);
 			if (!iface)
 				return NULL;
 
diff --git a/src/nm-manager.h b/src/nm-manager.h
index 966abce0..ad06e318 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2008 Novell, Inc.
  * Copyright (C) 2007 - 2010 Red Hat, Inc.
  */
@@ -77,6 +63,7 @@ GType nm_manager_get_type (void);
 NMManager *   nm_manager_setup                         (void);
 
 NMManager *   nm_manager_get                           (void);
+#define NM_MANAGER_GET (nm_manager_get ())
 
 gboolean      nm_manager_start                         (NMManager *manager,
                                                         GError **error);
@@ -162,6 +149,7 @@ void                nm_manager_device_route_metric_clear (NMManager *self,
 char *              nm_manager_get_connection_iface (NMManager *self,
                                                      NMConnection *connection,
                                                      NMDevice **out_parent,
+                                                     const char **out_parent_spec,
                                                      GError **error);
 
 const char *        nm_manager_iface_for_uuid          (NMManager *self,
@@ -203,4 +191,6 @@ void nm_manager_dbus_set_property_handle (NMDBusObject *obj,
 
 NMMetered nm_manager_get_metered (NMManager *self);
 
+void nm_manager_notify_device_availibility_maybe_changed (NMManager *self);
+
 #endif /* __NETWORKMANAGER_MANAGER_H__ */
diff --git a/src/nm-netns.c b/src/nm-netns.c
index c1ced153..3652384a 100644
--- a/src/nm-netns.c
+++ b/src/nm-netns.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
diff --git a/src/nm-netns.h b/src/nm-netns.h
index 126ef375..c65dd907 100644
--- a/src/nm-netns.h
+++ b/src/nm-netns.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c
index 38b53eab..b5c7bc02 100644
--- a/src/nm-pacrunner-manager.c
+++ b/src/nm-pacrunner-manager.c
@@ -1,20 +1,6 @@
-/* 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.
- *
- * (C) Copyright 2016 Atul Anand <atulhjp@gmail.com>.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Atul Anand <atulhjp@gmail.com>.
  */
 
 #include "nm-default.h"
diff --git a/src/nm-pacrunner-manager.h b/src/nm-pacrunner-manager.h
index f3f41e9c..d45a4a0a 100644
--- a/src/nm-pacrunner-manager.h
+++ b/src/nm-pacrunner-manager.h
@@ -1,21 +1,7 @@
-/* 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 2016 Atul Anand <atulhjp@gmail.com>.
- * Copyright 2016 - 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Atul Anand <atulhjp@gmail.com>.
+ * Copyright (C) 2016 - 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_PACRUNNER_MANAGER_H__
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 559babed..d322b2ef 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2013 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
@@ -179,7 +165,7 @@ static void
 _clear_ip6_subnet (gpointer key, gpointer value, gpointer user_data)
 {
 	NMPlatformIP6Address *subnet = value;
-	NMDevice *device = nm_manager_get_device_by_ifindex (nm_manager_get (),
+	NMDevice *device = nm_manager_get_device_by_ifindex (NM_MANAGER_GET,
 	                                                     GPOINTER_TO_INT (key));
 
 	if (device) {
@@ -1886,7 +1872,7 @@ device_state_changed (NMDevice *device,
 
 			switch (nm_device_state_reason_check (reason)) {
 			case NM_DEVICE_STATE_REASON_USER_REQUESTED:
-				 blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST;
+				blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST;
 				break;
 			case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
 				blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED;
diff --git a/src/nm-policy.h b/src/nm-policy.h
index dea500bf..23c50cff 100644
--- a/src/nm-policy.h
+++ b/src/nm-policy.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2004 - 2010 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
diff --git a/src/nm-proxy-config.c b/src/nm-proxy-config.c
index 5a7a85c0..feee4afe 100644
--- a/src/nm-proxy-config.c
+++ b/src/nm-proxy-config.c
@@ -1,20 +1,6 @@
-/* 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.
- *
- * (C) Copyright 2016 Atul Anand <atulhjp@gmail.com>.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Atul Anand <atulhjp@gmail.com>.
  */
 
 #include "nm-default.h"
diff --git a/src/nm-proxy-config.h b/src/nm-proxy-config.h
index 050e0d98..a23b80dd 100644
--- a/src/nm-proxy-config.h
+++ b/src/nm-proxy-config.h
@@ -1,20 +1,6 @@
-/* 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.
- *
- * (C) Copyright 2016 Atul Anand <atulhjp@gmail.com>.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Atul Anand <atulhjp@gmail.com>.
  */
 
 #ifndef __NETWORKMANAGER_PROXY_CONFIG_H__
diff --git a/src/nm-rfkill-manager.c b/src/nm-rfkill-manager.c
index c276fc77..28666fa0 100644
--- a/src/nm-rfkill-manager.c
+++ b/src/nm-rfkill-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2013 Red Hat, Inc.
  */
 
diff --git a/src/nm-rfkill-manager.h b/src/nm-rfkill-manager.h
index 5f2d3adb..4a3242e4 100644
--- a/src/nm-rfkill-manager.h
+++ b/src/nm-rfkill-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2008 Novell, Inc.
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c
index 8cb1b88d..5d5fe4c9 100644
--- a/src/nm-session-monitor.c
+++ b/src/nm-session-monitor.c
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 - 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2015 Red Hat, Inc.
  * Author: David Zeuthen <davidz@redhat.com>
  * Author: Dan Williams <dcbw@redhat.com>
  * Author: Matthias Clasen
diff --git a/src/nm-session-monitor.h b/src/nm-session-monitor.h
index 43f9eef9..64f91942 100644
--- a/src/nm-session-monitor.h
+++ b/src/nm-session-monitor.h
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 - 2010 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2010 Red Hat, Inc.
  * Author: David Zeuthen <davidz@redhat.com>
  * Author: Dan Williams <dcbw@redhat.com>
  */
diff --git a/src/nm-sleep-monitor.c b/src/nm-sleep-monitor.c
index c97f6f7d..c16dc074 100644
--- a/src/nm-sleep-monitor.c
+++ b/src/nm-sleep-monitor.c
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2012-2016 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 - 2016 Red Hat, Inc.
  * Author: Matthias Clasen <mclasen@redhat.com>
  */
 
diff --git a/src/nm-sleep-monitor.h b/src/nm-sleep-monitor.h
index da1fd8cf..c2f3ab81 100644
--- a/src/nm-sleep-monitor.h
+++ b/src/nm-sleep-monitor.h
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2012-2016 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 - 2016 Red Hat, Inc.
  * Author: Matthias Clasen <mclasen@redhat.com>
  */
 
diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h
index 1f4b87ef..e1d250dd 100644
--- a/src/nm-test-utils-core.h
+++ b/src/nm-test-utils-core.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * 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.
+ * Copyright (C) 2014 - 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_TEST_UTILS_CORE_H__
diff --git a/src/nm-types.h b/src/nm-types.h
index fae344f2..2f1ac2dc 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2012 - 2018 Red Hat, Inc.
  */
 
@@ -218,11 +204,20 @@ typedef enum {
 	NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1,
 } NMPObjectType;
 
+/**
+ * NMIPConfigMergeFlags:
+ * @NM_IP_CONFIG_MERGE_DEFAULT: no flags set
+ * @NM_IP_CONFIG_MERGE_NO_ROUTES: don't merge routes
+ * @NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES: don't merge default routes
+ * @NM_IP_CONFIG_MERGE_NO_DNS: don't merge DNS information
+ * @NM_IP_CONFIG_MERGE_EXTERNAL: mark new addresses as external
+ */
 typedef enum {
 	NM_IP_CONFIG_MERGE_DEFAULT                  = 0,
 	NM_IP_CONFIG_MERGE_NO_ROUTES                = (1LL << 0),
 	NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES        = (1LL << 1),
 	NM_IP_CONFIG_MERGE_NO_DNS                   = (1LL << 2),
+	NM_IP_CONFIG_MERGE_EXTERNAL                 = (1LL << 3),
 } NMIPConfigMergeFlags;
 
 /**
diff --git a/src/platform/linux/nl802154.h b/src/platform/linux/nl802154.h
index ddcee128..57646a39 100644
--- a/src/platform/linux/nl802154.h
+++ b/src/platform/linux/nl802154.h
@@ -3,7 +3,7 @@
 /*
  * 802.15.4 netlink interface public header
  *
- * Copyright 2014 Alexander Aring <aar@pengutronix.de>
+ * Copyright (C) 2014 Alexander Aring <aar@pengutronix.de>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 967aaee8..6aae808a 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -1,20 +1,6 @@
-/* nm-platform-fake.c - Fake platform interaction code for testing 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, 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) 2012–2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/platform/nm-fake-platform.h b/src/platform/nm-fake-platform.h
index d5be8c65..72245e67 100644
--- a/src/platform/nm-fake-platform.h
+++ b/src/platform/nm-fake-platform.h
@@ -1,19 +1,5 @@
-/* nm-fake-platform.h - Fake platform interaction code for testing 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, 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2012 Red Hat, Inc.
  */
 
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index d222527d..305ae52e 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -1,21 +1,8 @@
-/* nm-linux-platform.c - Linux kernel & udev network configuration layer
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2012 - 2018 Red Hat, Inc.
  */
+
 #include "nm-default.h"
 
 #include "nm-linux-platform.h"
@@ -868,14 +855,19 @@ _lookup_cached_link (const NMPCache *cache,
 static char *
 _linktype_read_devtype (int dirfd)
 {
-	char *contents = NULL;
+	gs_free char *contents = NULL;
 	char *cont, *end;
 
 	nm_assert (dirfd >= 0);
 
-	if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024,
-	                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                                &contents, NULL, NULL) < 0)
+	if (!nm_utils_file_get_contents (dirfd,
+	                                 "uevent",
+	                                 1*1024*1024,
+	                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+	                                 &contents,
+	                                 NULL,
+	                                 NULL,
+	                                 NULL))
 		return NULL;
 	for (cont = contents; cont; cont = end) {
 		end = strpbrk (cont, "\r\n");
@@ -884,10 +876,9 @@ _linktype_read_devtype (int dirfd)
 		if (strncmp (cont, DEVTYPE_PREFIX, NM_STRLEN (DEVTYPE_PREFIX)) == 0) {
 			cont += NM_STRLEN (DEVTYPE_PREFIX);
 			memmove (contents, cont, strlen (cont) + 1);
-			return contents;
+			return g_steal_pointer (&contents);
 		}
 	}
-	g_free (contents);
 	return NULL;
 }
 
@@ -4398,12 +4389,17 @@ static void
 _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value)
 {
 	GError *error = NULL;
-	char *contents;
+	gs_free char *contents = NULL;
 	gs_free char *value_escaped = g_strescape (value, NULL);
 
-	if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024,
-	                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                                &contents, NULL, &error) < 0) {
+	if (!nm_utils_file_get_contents (dirfd,
+	                                 path,
+	                                 1*1024*1024,
+	                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+	                                 &contents,
+	                                 NULL,
+	                                 NULL,
+	                                 &error)) {
 		_LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid ?: path, value_escaped, error->message);
 		g_clear_error (&error);
 		return;
@@ -4417,7 +4413,6 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c
 
 		_LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid ?: path, value_escaped, contents_escaped);
 	}
-	g_free (contents);
 }
 
 #define _log_dbg_sysctl_set(platform, pathid, dirfd, path, value) \
@@ -4833,7 +4828,7 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
 	GError *error = NULL;
-	char *contents;
+	gs_free char *contents = NULL;
 
 	ASSERT_SYSCTL_ARGS (pathid, dirfd, path);
 
@@ -4845,9 +4840,14 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
 		pathid = path;
 	}
 
-	if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024,
-	                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                                &contents, NULL, &error) < 0) {
+	if (!nm_utils_file_get_contents (dirfd,
+	                                 path,
+	                                 1*1024*1024,
+	                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+	                                 &contents,
+	                                 NULL,
+	                                 NULL,
+	                                 &error)) {
 		NMLogLevel log_level = LOGL_ERR;
 		int errsv = EBUSY;
 
@@ -4871,7 +4871,7 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
 	_log_dbg_sysctl_get (platform, pathid, contents);
 
 	/* errno is left undefined (as we don't return NULL). */
-	return contents;
+	return g_steal_pointer (&contents);
 }
 
 /*****************************************************************************/
diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h
index f08f91aa..90b9a17a 100644
--- a/src/platform/nm-linux-platform.h
+++ b/src/platform/nm-linux-platform.h
@@ -1,19 +1,5 @@
-/* nm-linux-platform.h - Linux kernel & udev network configuration layer
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2012 Red Hat, Inc.
  */
 
diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c
index da009d01..d133d351 100644
--- a/src/platform/nm-netlink.c
+++ b/src/platform/nm-netlink.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h
index c793f04a..0ac5b3b7 100644
--- a/src/platform/nm-netlink.h
+++ b/src/platform/nm-netlink.h
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/nm-platform-private.h b/src/platform/nm-platform-private.h
index 5a24eda0..a072af05 100644
--- a/src/platform/nm-platform-private.h
+++ b/src/platform/nm-platform-private.h
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index bc9ec11d..4f0da581 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h
index ae3f51e2..a62d828c 100644
--- a/src/platform/nm-platform-utils.h
+++ b/src/platform/nm-platform-utils.h
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 0a7acba9..6795dde7 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2012 - 2018 Red Hat, Inc.
  */
 
@@ -4152,11 +4138,12 @@ nm_platform_ip4_address_sync (NMPlatform *self,
 		}
 	}
 	ip4_addr_subnets_destroy_index (plat_subnets, plat_addresses);
-	ip4_addr_subnets_destroy_index (known_subnets, known_addresses);
 
 	if (!known_addresses)
 		return TRUE;
 
+	ip4_addr_subnets_destroy_index (known_subnets, known_addresses);
+
 	ifa_flags =   nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)
 	            ? IFA_F_NOPREFIXROUTE
 	            : 0;
@@ -6097,13 +6084,14 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *bu
 	str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time));
 
 	g_snprintf (buf, len,
-	            "%s/%d lft %s pref %s%s%s%s%s%s src %s",
+	            "%s/%d lft %s pref %s%s%s%s%s%s src %s%s",
 	            s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
 	            str_peer ?: "",
 	            str_dev,
 	            _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
 	            str_label,
-	            nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)));
+	            nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)),
+	            address->external ? " ext" : "");
 	g_free (str_peer);
 	return buf;
 }
@@ -6204,12 +6192,13 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu
 	str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time));
 
 	g_snprintf (buf, len,
-	            "%s/%d lft %s pref %s%s%s%s%s src %s",
+	            "%s/%d lft %s pref %s%s%s%s%s src %s%s",
 	            s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
 	            str_peer ?: "",
 	            str_dev,
 	            _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
-	            nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)));
+	            nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)),
+	            address->external ? " ext" : "");
 	g_free (str_peer);
 	return buf;
 }
@@ -7315,7 +7304,8 @@ nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashStat
 	                     obj->n_ifa_flags,
 	                     obj->plen,
 	                     obj->address,
-	                     obj->peer_address);
+	                     obj->peer_address,
+	                     NM_HASH_COMBINE_BOOLS (guint8, obj->external));
 	nm_hash_update_strarr (h, obj->label);
 }
 
@@ -7333,6 +7323,7 @@ nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4A
 	NM_CMP_FIELD (a, b, preferred);
 	NM_CMP_FIELD (a, b, n_ifa_flags);
 	NM_CMP_FIELD_STR (a, b, label);
+	NM_CMP_FIELD_UNSAFE (a, b, external);
 	return 0;
 }
 
@@ -7348,7 +7339,8 @@ nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashStat
 	                     obj->n_ifa_flags,
 	                     obj->plen,
 	                     obj->address,
-	                     obj->peer_address);
+	                     obj->peer_address,
+	                     NM_HASH_COMBINE_BOOLS (guint8, obj->external));
 }
 
 int
@@ -7368,6 +7360,7 @@ nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6A
 	NM_CMP_FIELD (a, b, lifetime);
 	NM_CMP_FIELD (a, b, preferred);
 	NM_CMP_FIELD (a, b, n_ifa_flags);
+	NM_CMP_FIELD_UNSAFE (a, b, external);
 	return 0;
 }
 
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 44733809..4bd8e34d 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 - 2018 Red Hat, Inc.
  */
 
@@ -320,6 +306,8 @@ typedef enum {
 	guint32 n_ifa_flags; \
 	\
 	guint8 plen; \
+	\
+	bool external:1; \
 	;
 
 /**
@@ -508,7 +496,7 @@ struct _NMPlatformIP4Route {
 
 	/* The bitwise inverse of the route scope rtm_scope. It is inverted so that the
 	 * default value (RT_SCOPE_NOWHERE) is zero. Use nm_platform_route_scope_inv()
-	 * to convert back and forth between the inverese representation and the
+	 * to convert back and forth between the inverse representation and the
 	 * real value.
 	 *
 	 * rtm_scope is part of the primary key for IPv4 routes. When deleting a route,
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c
index b33d86fb..78f76cc7 100644
--- a/src/platform/nmp-netns.c
+++ b/src/platform/nmp-netns.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h
index 3c0ca74b..a74e2319 100644
--- a/src/platform/nmp-netns.h
+++ b/src/platform/nmp-netns.h
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 5cbf9428..97aa4f28 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 - 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index cea5f958..d52cc132 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 - 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/nmp-rules-manager.c b/src/platform/nmp-rules-manager.c
index f982f04c..a1d724c5 100644
--- a/src/platform/nmp-rules-manager.c
+++ b/src/platform/nmp-rules-manager.c
@@ -1,19 +1,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.
- */
+// SPDX-License-Identifier: LGPL-2.1+
 
 #include "nm-default.h"
 
diff --git a/src/platform/nmp-rules-manager.h b/src/platform/nmp-rules-manager.h
index 645df5c2..cf29ff45 100644
--- a/src/platform/nmp-rules-manager.h
+++ b/src/platform/nmp-rules-manager.h
@@ -1,19 +1,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.
- */
+// SPDX-License-Identifier: LGPL-2.1+
 
 #ifndef __NMP_RULES_MANAGER_H__
 #define __NMP_RULES_MANAGER_H__
diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build
index aaee8964..6f7173b6 100644
--- a/src/platform/tests/meson.build
+++ b/src/platform/tests/meson.build
@@ -1,21 +1,25 @@
+test_fake_c_flags = test_c_flags + ['-DSETUP=nm_fake_platform_setup']
+test_linux_c_flags = test_c_flags + ['-DSETUP=nm_linux_platform_setup']
+
 test_units = [
-  [ 'test-address-fake',     'test-address.c',          test_nm_dep_fake,  default_test_timeout ],
-  [ 'test-address-linux',    'test-address.c',          test_nm_dep_linux, default_test_timeout ],
-  [ 'test-cleanup-fake',     'test-cleanup.c',          test_nm_dep_fake,  default_test_timeout ],
-  [ 'test-cleanup-linux',    'test-cleanup.c',          test_nm_dep_linux, default_test_timeout ],
-  [ 'test-link-fake',        'test-link.c',             test_nm_dep_fake,  default_test_timeout ],
-  [ 'test-link-linux',       'test-link.c',             test_nm_dep_linux, 900                  ],
-  [ 'test-nmp-object',       'test-nmp-object.c',       test_nm_dep,       default_test_timeout ],
-  [ 'test-platform-general', 'test-platform-general.c', test_nm_dep,       default_test_timeout ],
-  [ 'test-route-fake',       'test-route.c',            test_nm_dep_fake,  default_test_timeout ],
-  [ 'test-route-linux',      'test-route.c',            test_nm_dep_linux, default_test_timeout ],
+  ['test-address-fake', 'test-address.c', test_fake_c_flags, default_test_timeout],
+  ['test-address-linux', 'test-address.c', test_linux_c_flags, default_test_timeout],
+  ['test-cleanup-fake', 'test-cleanup.c', test_fake_c_flags, default_test_timeout],
+  ['test-cleanup-linux', 'test-cleanup.c', test_linux_c_flags, default_test_timeout],
+  ['test-link-fake', 'test-link.c', test_fake_c_flags, default_test_timeout],
+  ['test-link-linux', 'test-link.c', test_linux_c_flags, 900],
+  ['test-nmp-object', 'test-nmp-object.c', test_c_flags, default_test_timeout],
+  ['test-platform-general', 'test-platform-general.c', test_c_flags, default_test_timeout],
+  ['test-route-fake', 'test-route.c', test_fake_c_flags, default_test_timeout],
+  ['test-route-linux', 'test-route.c', test_linux_c_flags, default_test_timeout],
 ]
 
 foreach test_unit: test_units
   exe = executable(
     test_unit[0],
     test_unit[1],
-    dependencies: test_unit[2],
+    dependencies: libnetwork_manager_test_dep,
+    c_args: test_unit[2],
   )
   test(
     'platform/' + test_unit[0],
@@ -25,8 +29,11 @@ foreach test_unit: test_units
   )
 endforeach
 
+name = 'monitor'
+
 executable(
-  'monitor',
-  'monitor.c',
-  dependencies: test_nm_dep,
+  name,
+  name + '.c',
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
 )
diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c
index 6a5e0e16..aec29b75 100644
--- a/src/platform/tests/monitor.c
+++ b/src/platform/tests/monitor.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c
index 3a3e9009..26072400 100644
--- a/src/platform/tests/test-address.c
+++ b/src/platform/tests/test-address.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c
index 70d64c15..34175c01 100644
--- a/src/platform/tests/test-cleanup.c
+++ b/src/platform/tests/test-cleanup.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2016 Red Hat, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index cd0bef45..b93213ef 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2016 - 2017 Red Hat, Inc.
+ * Copyright (C) 2016 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index e4eaec29..c571df6a 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2016 - 2017 Red Hat, Inc.
+ * Copyright (C) 2016 - 2017 Red Hat, Inc.
  */
 
 #include <stdlib.h>
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 27ec3f07..37e1cde5 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2016 Red Hat, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -476,9 +463,9 @@ _system (const char *cmd)
 static void
 test_bond (void)
 {
-	if (nmtstp_is_root_test () &&
-	    !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) &&
-	    _system("modprobe --show bonding") != 0) {
+	if (   nmtstp_is_root_test ()
+	    && !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR)
+	    && _system("modprobe --show bonding") != 0) {
 		g_test_skip ("Skipping test for bonding: bonding module not available");
 		return;
 	}
@@ -489,6 +476,20 @@ test_bond (void)
 static void
 test_team (void)
 {
+	int r;
+
+	if (nmtstp_is_root_test ()) {
+		r = nm_platform_link_team_add (NM_PLATFORM_GET, "nm-team-check", NULL);
+
+		if (r < 0) {
+			g_assert_cmpint (r, ==, -EOPNOTSUPP);
+			g_test_skip ("Skipping test for teaming: team module not functioning");
+			return;
+		}
+
+		nmtstp_link_delete (NM_PLATFORM_GET, -1, -1, "nm-team-check", FALSE);
+	}
+
 	test_software (NM_LINK_TYPE_TEAM, "team");
 }
 
@@ -2886,9 +2887,14 @@ test_sysctl_rename (void)
 	case 0: {
 		gs_free char *c = NULL;
 
-		if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024,
-		                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-		                                &c, NULL, NULL) < 0)
+		if (!nm_utils_file_get_contents (dirfd,
+		                                 "ifindex",
+		                                 1*1024*1024,
+		                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+		                                 &c,
+		                                 NULL,
+		                                 NULL,
+		                                 NULL))
 			g_assert_not_reached();
 		g_assert_cmpint (ifindex[0], ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1));
 		break;
@@ -2952,9 +2958,14 @@ test_sysctl_netns_switch (void)
 	{
 		gs_free char *c = NULL;
 
-		if (nm_utils_file_get_contents (dirfd, "ifindex", 0,
-		                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-		                                &c, NULL, NULL) < 0)
+		if (!nm_utils_file_get_contents (dirfd,
+		                                 "ifindex",
+		                                 0,
+		                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+		                                 &c,
+		                                 NULL,
+		                                 NULL,
+		                                 NULL))
 			g_assert_not_reached();
 		g_assert_cmpint (ifindex, ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1));
 	}
@@ -2997,11 +3008,14 @@ test_sysctl_netns_switch (void)
 	{
 		gs_free char *c = NULL;
 
-		if (nm_utils_file_get_contents (-1,
-		                                nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME),
-		                                0,
-		                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-		                                &c, NULL, NULL) < 0)
+		if (!nm_utils_file_get_contents (-1,
+		                                 nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME),
+		                                 0,
+		                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+		                                 &c,
+		                                 NULL,
+		                                 NULL,
+		                                 NULL))
 			ifindex_tmp = -1;
 		else
 			ifindex_tmp = _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -2);
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index 08bde437..8ecfac8a 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 - 2017 Red Hat, Inc.
  */
 
diff --git a/src/platform/tests/test-platform-general.c b/src/platform/tests/test-platform-general.c
index 2b723810..255210f2 100644
--- a/src/platform/tests/test-platform-general.c
+++ b/src/platform/tests/test-platform-general.c
@@ -1,19 +1,5 @@
-/* nm-platform.c - Handle runtime kernel networking configuration
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 - 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 44bfbc58..f8b1bb9a 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2016 - 2017 Red Hat, Inc.
+ * Copyright (C) 2016 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c
index 36901f83..afeb3b02 100644
--- a/src/platform/wifi/nm-wifi-utils-nl80211.c
+++ b/src/platform/wifi/nm-wifi-utils-nl80211.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  * Copyright (C) 2011 Intel Corporation. All rights reserved.
@@ -913,6 +899,9 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 	if (tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED])
 		info->can_wowlan = TRUE;
 
+	if (tb[NL80211_ATTR_SUPPORT_IBSS_RSN])
+		info->caps |= NM_WIFI_DEVICE_CAP_IBSS_RSN;
+
 	info->success = TRUE;
 
 	return NL_SKIP;
diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.h b/src/platform/wifi/nm-wifi-utils-nl80211.h
index a4c9ca55..45876534 100644
--- a/src/platform/wifi/nm-wifi-utils-nl80211.h
+++ b/src/platform/wifi/nm-wifi-utils-nl80211.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 Intel Corporation. All rights reserved.
  * Copyright (C) 2018 Red Hat, Inc.
  */
diff --git a/src/platform/wifi/nm-wifi-utils-private.h b/src/platform/wifi/nm-wifi-utils-private.h
index 4cae22e5..53816744 100644
--- a/src/platform/wifi/nm-wifi-utils-private.h
+++ b/src/platform/wifi/nm-wifi-utils-private.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 - 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/wifi/nm-wifi-utils-wext.c b/src/platform/wifi/nm-wifi-utils-wext.c
index d19cf8a5..68924a35 100644
--- a/src/platform/wifi/nm-wifi-utils-wext.c
+++ b/src/platform/wifi/nm-wifi-utils-wext.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
diff --git a/src/platform/wifi/nm-wifi-utils-wext.h b/src/platform/wifi/nm-wifi-utils-wext.h
index 70cae966..ce76f68d 100644
--- a/src/platform/wifi/nm-wifi-utils-wext.h
+++ b/src/platform/wifi/nm-wifi-utils-wext.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2011 - 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/wifi/nm-wifi-utils.c b/src/platform/wifi/nm-wifi-utils.c
index 1e42db6a..39ce9b09 100644
--- a/src/platform/wifi/nm-wifi-utils.c
+++ b/src/platform/wifi/nm-wifi-utils.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
diff --git a/src/platform/wifi/nm-wifi-utils.h b/src/platform/wifi/nm-wifi-utils.h
index 2e89053b..bc0d88c8 100644
--- a/src/platform/wifi/nm-wifi-utils.h
+++ b/src/platform/wifi/nm-wifi-utils.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c
index 0afc2a4d..231e6e2c 100644
--- a/src/platform/wpan/nm-wpan-utils.c
+++ b/src/platform/wpan/nm-wpan-utils.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/platform/wpan/nm-wpan-utils.h b/src/platform/wpan/nm-wpan-utils.h
index 1b54ec49..deafeb97 100644
--- a/src/platform/wpan/nm-wpan-utils.h
+++ b/src/platform/wpan/nm-wpan-utils.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/ppp/meson.build b/src/ppp/meson.build
index 04c539e3..0c458768 100644
--- a/src/ppp/meson.build
+++ b/src/ppp/meson.build
@@ -1,39 +1,30 @@
 name = 'nm-pppd-plugin'
 
-deps = [
-  dl_dep,
-  libnm_core_dep,
+c_flags = [
+  '-DG_LOG_DOMAIN="@0@"'.format(name),
+  '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB',
 ]
 
 nm_pppd_plugin = shared_module(
   name,
   name_prefix: '',
   sources: name + '.c',
-  include_directories: src_inc,
-  dependencies: deps,
-  c_args: [
-    '-DG_LOG_DOMAIN="@0@"'.format(name),
-    '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB',
-  ],
+  dependencies: libnm_core_nm_default_dep,
+  c_args: c_flags,
   install: true,
   install_dir: pppd_plugin_dir,
 )
 
 name = 'nm-ppp-plugin'
 
-deps = [
-  nm_dep,
-]
-
 linker_script = join_paths(meson.current_source_dir(), 'nm-ppp-plugin.ver')
 
 core_plugins += shared_module(
   name,
   sources: 'nm-ppp-manager.c',
-  dependencies: deps,
-  link_args: [
-    '-Wl,--version-script,@0@'.format(linker_script),
-  ],
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
+  link_args: '-Wl,--version-script,@0@'.format(linker_script),
   link_depends: linker_script,
   install: true,
   install_dir: nm_plugindir,
diff --git a/src/ppp/nm-ppp-manager-call.c b/src/ppp/nm-ppp-manager-call.c
index c134a2b2..11520ec9 100644
--- a/src/ppp/nm-ppp-manager-call.c
+++ b/src/ppp/nm-ppp-manager-call.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/ppp/nm-ppp-manager-call.h b/src/ppp/nm-ppp-manager-call.h
index 6561a388..96fb6483 100644
--- a/src/ppp/nm-ppp-manager-call.h
+++ b/src/ppp/nm-ppp-manager-call.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c
index 3708b940..f649e0cb 100644
--- a/src/ppp/nm-ppp-manager.c
+++ b/src/ppp/nm-ppp-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 - 2012 Red Hat, Inc.
  */
@@ -1226,7 +1212,7 @@ _ppp_manager_stop (NMPPPManager *self,
 		/* No PID. There is nothing to kill, however, invoke the callback in
 		 * an idle handler.
 		 *
-		 * Note that we don't register nm_shutdown_wait_obj_register().
+		 * Note that we don't register nm_shutdown_wait_obj_register_object().
 		 * In order for shutdown to work properly, the caller must always
 		 * explicitly cancel the action to go down. With the idle-handler,
 		 * cancelling the handle completes the request. */
@@ -1238,7 +1224,7 @@ _ppp_manager_stop (NMPPPManager *self,
 	 * until the process terminated. We do that, by registering an object
 	 * that delays shutdown. */
 	handle->shutdown_waitobj = g_object_new (G_TYPE_OBJECT, NULL);
-	nm_shutdown_wait_obj_register (handle->shutdown_waitobj, "ppp-manager-wait-kill-pppd");
+	nm_shutdown_wait_obj_register_object (handle->shutdown_waitobj, "ppp-manager-wait-kill-pppd");
 	nm_utils_kill_child_async (nm_steal_int (&priv->pid),
 	                           SIGTERM, LOGD_PPP, "pppd",
 	                           NM_SHUTDOWN_TIMEOUT_MS,
diff --git a/src/ppp/nm-ppp-manager.h b/src/ppp/nm-ppp-manager.h
index d2285e3e..8657367f 100644
--- a/src/ppp/nm-ppp-manager.h
+++ b/src/ppp/nm-ppp-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 - 2016 Red Hat, Inc.
  */
diff --git a/src/ppp/nm-ppp-plugin-api.h b/src/ppp/nm-ppp-plugin-api.h
index 7177a7d6..f2d4f7be 100644
--- a/src/ppp/nm-ppp-plugin-api.h
+++ b/src/ppp/nm-ppp-plugin-api.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/ppp/nm-ppp-status.h b/src/ppp/nm-ppp-status.h
index a57685a1..f817d7cb 100644
--- a/src/ppp/nm-ppp-status.h
+++ b/src/ppp/nm-ppp-status.h
@@ -1,21 +1,7 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008-2016 Red Hat, Inc.
+ * Copyright (C) 2008 - 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_PPP_STATUS_H__
diff --git a/src/ppp/nm-pppd-plugin.c b/src/ppp/nm-pppd-plugin.c
index f0b9027c..db9a2b67 100644
--- a/src/ppp/nm-pppd-plugin.c
+++ b/src/ppp/nm-pppd-plugin.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 Red Hat, Inc.
  */
diff --git a/src/ppp/nm-pppd-plugin.h b/src/ppp/nm-pppd-plugin.h
index e0d691bf..e69bc2ae 100644
--- a/src/ppp/nm-pppd-plugin.h
+++ b/src/ppp/nm-pppd-plugin.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 - 2014 Red Hat, Inc.
  */
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index db0e021c..ecca0eb3 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 - 2013 Red Hat, Inc.
  */
 
@@ -93,13 +79,13 @@ NM_DEFINE_SINGLETON_GETTER (NMAgentManager, nm_agent_manager_get, NM_TYPE_AGENT_
             if (!(self)) \
                 g_snprintf (__prefix1, sizeof (__prefix1), "%s%s", ""_NMLOG_PREFIX_NAME"", "[]"); \
             else if ((self) != singleton_instance) \
-                g_snprintf (__prefix1, sizeof (__prefix1), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
+                g_snprintf (__prefix1, sizeof (__prefix1), "%s["NM_HASH_OBFUSCATE_PTR_FMT"]", ""_NMLOG_PREFIX_NAME"", NM_HASH_OBFUSCATE_PTR (self)); \
             else \
                 g_strlcpy (__prefix1, _NMLOG_PREFIX_NAME, sizeof (__prefix1)); \
             if (__agent) { \
                 g_snprintf (__prefix2, sizeof (__prefix2), \
-                            ": req[%p, %s]", \
-                            __agent, \
+                            ": agent["NM_HASH_OBFUSCATE_PTR_FMT",%s]", \
+                            NM_HASH_OBFUSCATE_PTR (__agent), \
                             nm_secret_agent_get_description (__agent)); \
             } else \
                 __prefix2[0] = '\0'; \
@@ -109,9 +95,9 @@ NM_DEFINE_SINGLETON_GETTER (NMAgentManager, nm_agent_manager_get, NM_TYPE_AGENT_
         } \
     } G_STMT_END
 
-#define LOG_REQ_FMT          "[%p/%s%s%s%s%s%s]"
+#define LOG_REQ_FMT          "["NM_HASH_OBFUSCATE_PTR_FMT"/%s%s%s%s%s%s]"
 #define LOG_REQ_ARG(req) \
-	(req), \
+	NM_HASH_OBFUSCATE_PTR (req), \
 	NM_PRINT_FMT_QUOTE_STRING ((req)->detail), \
 	NM_PRINT_FMT_QUOTED (((req)->request_type == REQUEST_TYPE_CON_GET) && (req)->con.get.setting_name, \
 	                     "/\"", (req)->con.get.setting_name, "\"", \
@@ -539,12 +525,10 @@ request_free (Request *req)
 	if (req->idle_id)
 		g_source_remove (req->idle_id);
 
-	if (req->current && req->current_call_id) {
-		/* cancel-secrets invokes the done-callback synchronously -- in which case
-		 * the handler just return.
-		 * Hence, we can proceed to free @req... */
-		nm_secret_agent_cancel_secrets (req->current, req->current_call_id);
-	}
+	/* cancel-secrets invokes the done-callback synchronously -- in which case
+	 * the handler just return.
+	 * Hence, we can proceed to free @req... */
+	nm_secret_agent_cancel_call (req->current, req->current_call_id);
 
 	g_object_unref (req->subject);
 
@@ -742,12 +726,9 @@ request_next_agent (Request *req)
 
 	self = req->self;
 
-	if (req->current) {
-		if (req->current_call_id)
-			nm_secret_agent_cancel_secrets (req->current, req->current_call_id);
-		g_clear_object (&req->current);
-	}
+	nm_secret_agent_cancel_call (req->current, req->current_call_id);
 	nm_assert (!req->current_call_id);
+	g_clear_object (&req->current);
 
 	if (req->pending) {
 		/* Send the request to the next agent */
@@ -882,10 +863,8 @@ _con_get_request_done (NMSecretAgent *agent,
 			req_complete_error (req, error);
 			g_error_free (error);
 		} else {
-			if (req->current_call_id) {
-				/* Tell the failed agent we're no longer interested. */
-				nm_secret_agent_cancel_secrets (req->current, req->current_call_id);
-			}
+			/* Tell the failed agent we're no longer interested. */
+			nm_secret_agent_cancel_call (req->current, req->current_call_id);
 
 			/* Try the next agent */
 			request_next_agent (req);
diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h
index 949ab6bc..5200d241 100644
--- a/src/settings/nm-agent-manager.h
+++ b/src/settings/nm-agent-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 - 2011 Red Hat, Inc.
  */
 
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index 2aa1476d..74bd9b2f 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 - 2011 Red Hat, Inc.
  */
 
@@ -24,9 +10,9 @@
 #include <sys/types.h>
 #include <pwd.h>
 
+#include "nm-glib-aux/nm-c-list.h"
 #include "nm-glib-aux/nm-dbus-aux.h"
 #include "nm-dbus-interface.h"
-#include "nm-dbus-manager.h"
 #include "nm-core-internal.h"
 #include "nm-auth-subject.h"
 #include "nm-simple-connection.h"
@@ -35,30 +21,32 @@
 
 /*****************************************************************************/
 
+#define METHOD_GET_SECRETS        "GetSecrets"
+#define METHOD_CANCEL_GET_SECRETS "CancelGetSecrets"
+#define METHOD_SAVE_SECRETS       "SaveSecrets"
+#define METHOD_DELETE_SECRETS     "DeleteSecrets"
+
 enum {
 	DISCONNECTED,
 
 	LAST_SIGNAL
 };
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
+	CList permissions;
 	char *description;
 	NMAuthSubject *subject;
 	char *identifier;
 	char *owner_username;
 	char *dbus_owner;
-	NMSecretAgentCapabilities capabilities;
-	GSList *permissions;
-	GDBusProxy *proxy;
-	NMDBusManager *bus_mgr;
-	GDBusConnection *connection;
+	GDBusConnection *dbus_connection;
+	GCancellable *name_owner_cancellable;
 	CList requests;
-	union {
-		gulong obj_signal;
-		guint dbus_signal;
-	} on_disconnected_id;
-	bool connection_is_private:1;
+	NMSecretAgentCapabilities capabilities;
+	guint name_owner_changed_id;
+	bool shutdown_wait_obj_registered:1;
 } NMSecretAgentPrivate;
 
 struct _NMSecretAgent {
@@ -81,25 +69,51 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT)
 #define _NMLOG(level, ...) \
     G_STMT_START { \
         if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
-            char __prefix[32]; \
+            char _prefix[64]; \
             \
-            if ((self)) \
-                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
-            else \
-                g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
+            if ((self)) { \
+                g_snprintf (_prefix, \
+                            sizeof (_prefix), \
+                            _NMLOG_PREFIX_NAME"["NM_HASH_OBFUSCATE_PTR_FMT"]", \
+                            NM_HASH_OBFUSCATE_PTR (self)); \
+            } else \
+                g_strlcpy (_prefix, _NMLOG_PREFIX_NAME, sizeof (_prefix)); \
+            \
+            _nm_log ((level), \
+                     (_NMLOG_DOMAIN), \
+                     0, \
+                     NULL, \
+                     NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-                     __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+                     _prefix \
+                     _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
     } G_STMT_END
 
-#define LOG_REQ_FMT          "req[%p,%s,%s%s%s%s]"
-#define LOG_REQ_ARG(req)     (req), (req)->dbus_command, NM_PRINT_FMT_QUOTE_STRING ((req)->path), ((req)->cancellable ? "" : " (cancelled)")
+#define _NMLOG2(level, call_id, ...) \
+    G_STMT_START { \
+        NMSecretAgentCallId *const _call_id = (call_id); \
+        \
+        nm_assert (_call_id); \
+        \
+        nm_log ((level), \
+                (_NMLOG_DOMAIN), \
+                NULL, \
+                NULL, \
+                "%s["NM_HASH_OBFUSCATE_PTR_FMT"] request ["NM_HASH_OBFUSCATE_PTR_FMT",%s,%s%s%s%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME, \
+                NM_HASH_OBFUSCATE_PTR (_call_id->self), \
+                NM_HASH_OBFUSCATE_PTR (_call_id), \
+                _call_id->method_name, \
+                NM_PRINT_FMT_QUOTE_STRING (_call_id->path), \
+                (_call_id->cancellable ? "" : " (cancelled)") \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
 
 /*****************************************************************************/
 
 NM_UTILS_FLAGS2STR_DEFINE_STATIC (_capabilities_to_string, NMSecretAgentCapabilities,
-	NM_UTILS_FLAGS2STR (NM_SECRET_AGENT_CAPABILITY_NONE, "none"),
+	NM_UTILS_FLAGS2STR (NM_SECRET_AGENT_CAPABILITY_NONE,      "none"),
 	NM_UTILS_FLAGS2STR (NM_SECRET_AGENT_CAPABILITY_VPN_HINTS, "vpn-hints"),
 );
 
@@ -107,69 +121,100 @@ NM_UTILS_FLAGS2STR_DEFINE_STATIC (_capabilities_to_string, NMSecretAgentCapabili
 
 struct _NMSecretAgentCallId {
 	CList lst;
-	NMSecretAgent *agent;
+	NMSecretAgent *self;
 	GCancellable *cancellable;
 	char *path;
-	const char *dbus_command;
+	const char *method_name;
 	char *setting_name;
-	gboolean is_get_secrets;
 	NMSecretAgentCallback callback;
 	gpointer callback_data;
 };
 
 static NMSecretAgentCallId *
-request_new (NMSecretAgent *self,
-             const char *dbus_command, /* this must be a static string. */
-             const char *path,
-             const char *setting_name,
-             NMSecretAgentCallback callback,
-             gpointer callback_data)
+_call_id_new (NMSecretAgent *self,
+              const char *method_name, /* this must be a static string. */
+              const char *path,
+              const char *setting_name,
+              NMSecretAgentCallback callback,
+              gpointer callback_data)
 {
-	NMSecretAgentCallId *r;
-
-	r = g_slice_new0 (NMSecretAgentCallId);
-	r->agent = self;
-	r->path = g_strdup (path);
-	r->setting_name = g_strdup (setting_name);
-	r->dbus_command = dbus_command,
-	r->callback = callback;
-	r->callback_data = callback_data;
-	r->cancellable = g_cancellable_new ();
-	c_list_link_tail (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests,
-	                  &r->lst);
-	_LOGt ("request "LOG_REQ_FMT": created", LOG_REQ_ARG (r));
-	return r;
+	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+	NMSecretAgentCallId *call_id;
+
+	call_id = g_slice_new (NMSecretAgentCallId);
+	*call_id = (NMSecretAgentCallId) {
+		.self              = g_object_ref (self),
+		.path              = g_strdup (path),
+		.setting_name      = g_strdup (setting_name),
+		.method_name       = method_name,
+		.callback          = callback,
+		.callback_data     = callback_data,
+		.cancellable       = g_cancellable_new (),
+	};
+	c_list_link_tail (&priv->requests, &call_id->lst);
+
+	_LOG2T (call_id, "new request...");
+
+	if (!priv->shutdown_wait_obj_registered) {
+		/* self has async requests (that keep self alive). As long as
+		 * we have pending requests, shutdown is blocked. */
+		priv->shutdown_wait_obj_registered = TRUE;
+		nm_shutdown_wait_obj_register_object (G_OBJECT (self), "secret-agent");
+	}
+
+	return call_id;
 }
-#define request_new(self,dbus_command,path,setting_name,callback,callback_data) request_new(self,""dbus_command"",path,setting_name,callback,callback_data)
+
+#define _call_id_new(self, method_name, path, setting_name, callback, callback_data) _call_id_new(self, ""method_name"", path, setting_name, callback, callback_data)
 
 static void
-request_free (NMSecretAgentCallId *r)
+_call_id_free (NMSecretAgentCallId *call_id)
 {
-	NMSecretAgent *self = r->agent;
-
-	_LOGt ("request "LOG_REQ_FMT": destroyed", LOG_REQ_ARG (r));
-	c_list_unlink_stale (&r->lst);
-	g_free (r->path);
-	g_free (r->setting_name);
-	if (r->cancellable)
-		g_object_unref (r->cancellable);
-	g_slice_free (NMSecretAgentCallId, r);
+	c_list_unlink_stale (&call_id->lst);
+	g_free (call_id->path);
+	g_free (call_id->setting_name);
+	nm_g_object_unref (call_id->cancellable);
+	g_object_unref (call_id->self);
+	nm_g_slice_free (call_id);
 }
 
-static gboolean
-request_check_return (NMSecretAgentCallId *r)
+static void
+_call_id_invoke_callback (NMSecretAgentCallId *call_id,
+                          GVariant *secrets,
+                          GError *error,
+                          gboolean cancelled,
+                          gboolean free_call_id)
 {
-	if (!r->cancellable)
-		return FALSE;
+	gs_free_error GError *error_cancelled = NULL;
 
-	g_return_val_if_fail (NM_IS_SECRET_AGENT (r->agent), FALSE);
+	nm_assert (call_id);
+	nm_assert (!c_list_is_empty (&call_id->lst));
 
-	nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (r->agent)->requests,
-	                            &r->lst));
+	c_list_unlink (&call_id->lst);
 
-	c_list_unlink (&r->lst);
+	if (cancelled) {
+		nm_assert (!secrets);
+		nm_assert (!error);
+		if (call_id->callback) {
+			nm_utils_error_set_cancelled (&error_cancelled, FALSE, "NMSecretAgent");
+			error = error_cancelled;
+		}
+		_LOG2T (call_id, "cancelled");
+	} else if (error) {
+		nm_assert (!secrets);
+		_LOG2T (call_id, "completed with failure: %s", error->message);
+	} else {
+		nm_assert (   !secrets
+		           || g_variant_is_of_type (secrets, G_VARIANT_TYPE ("a{sa{sv}}")));
+		nm_assert ((!!secrets) == nm_streq0 (call_id->method_name, METHOD_GET_SECRETS));
+		_LOG2T (call_id, "completed successfully");
+	}
+
+	if (call_id->callback)
+		call_id->callback (call_id->self, call_id, secrets, error, call_id->callback_data);
 
-	return TRUE;
+	if (free_call_id)
+		_call_id_free (call_id);
 }
 
 /*****************************************************************************/
@@ -200,6 +245,8 @@ nm_secret_agent_get_description (NMSecretAgent *agent)
 	return priv->description;
 }
 
+/*****************************************************************************/
+
 const char *
 nm_secret_agent_get_dbus_owner (NMSecretAgent *agent)
 {
@@ -256,6 +303,8 @@ nm_secret_agent_get_subject (NMSecretAgent *agent)
 	return NM_SECRET_AGENT_GET_PRIVATE (agent)->subject;
 }
 
+/*****************************************************************************/
+
 /**
  * nm_secret_agent_add_permission:
  * @agent: A #NMSecretAgent.
@@ -269,31 +318,25 @@ nm_secret_agent_add_permission (NMSecretAgent *agent,
                                 gboolean allowed)
 {
 	NMSecretAgentPrivate *priv;
-	GSList *iter;
+	NMCListElem *elem;
 
 	g_return_if_fail (agent != NULL);
 	g_return_if_fail (permission != NULL);
 
 	priv = NM_SECRET_AGENT_GET_PRIVATE (agent);
 
-	/* Check if the permission is already in the list */
-	for (iter = priv->permissions; iter; iter = g_slist_next (iter)) {
-		if (g_strcmp0 (permission, iter->data) == 0) {
-			/* If the permission is no longer allowed, remove it from the
-			 * list.  If it is now allowed, do nothing since it's already
-			 * in the list.
-			 */
-			if (allowed == FALSE) {
-				g_free (iter->data);
-				priv->permissions = g_slist_delete_link (priv->permissions, iter);
-			}
-			return;
-		}
+	elem = nm_c_list_elem_find_first (&priv->permissions, p, nm_streq (p, permission));
+
+	if (elem) {
+		if (!allowed)
+			nm_c_list_elem_free_full (elem, g_free);
+		return;
 	}
 
-	/* New permission that's allowed */
-	if (allowed)
-		priv->permissions = g_slist_prepend (priv->permissions, g_strdup (permission));
+	if (allowed) {
+		c_list_link_tail (&priv->permissions,
+		                  &nm_c_list_elem_new_stale (g_strdup (permission))->lst);
+	}
 }
 
 /**
@@ -310,51 +353,48 @@ nm_secret_agent_add_permission (NMSecretAgent *agent,
 gboolean
 nm_secret_agent_has_permission (NMSecretAgent *agent, const char *permission)
 {
-	NMSecretAgentPrivate *priv;
-	GSList *iter;
-
 	g_return_val_if_fail (agent != NULL, FALSE);
 	g_return_val_if_fail (permission != NULL, FALSE);
 
-	priv = NM_SECRET_AGENT_GET_PRIVATE (agent);
-
-	/* Check if the permission is already in the list */
-	for (iter = priv->permissions; iter; iter = g_slist_next (iter)) {
-		if (g_strcmp0 (permission, iter->data) == 0)
-			return TRUE;
-	}
-	return FALSE;
+	return !!nm_c_list_elem_find_first (&NM_SECRET_AGENT_GET_PRIVATE (agent)->permissions,
+	                                    p, nm_streq (p, permission));
 }
 
 /*****************************************************************************/
 
 static void
-get_callback (GObject *proxy,
-              GAsyncResult *result,
-              gpointer user_data)
+_dbus_call_cb (GObject *source,
+               GAsyncResult *result,
+               gpointer user_data)
 {
-	NMSecretAgentCallId *r = user_data;
-
-	if (request_check_return (r)) {
-		NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
-		gs_free_error GError *error = NULL;
-		gs_unref_variant GVariant *ret = NULL;
-		gs_unref_variant GVariant *secrets = NULL;
-
-		ret = _nm_dbus_proxy_call_finish (priv->proxy, result, G_VARIANT_TYPE ("(a{sa{sv}})"), &error);
-		if (!ret)
-			g_dbus_error_strip_remote_error (error);
-		else {
+	NMSecretAgentCallId *call_id;
+	gs_unref_variant GVariant *ret = NULL;
+	gs_unref_variant GVariant *secrets = NULL;
+	gs_free_error GError *error = NULL;
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+
+	if (   !ret
+	    && nm_utils_error_is_cancelled (error, FALSE))
+		return;
+
+	call_id = user_data;
+
+	if (!ret)
+		g_dbus_error_strip_remote_error (error);
+	else {
+		if (nm_streq (call_id->method_name, METHOD_GET_SECRETS)) {
 			g_variant_get (ret,
 			               "(@a{sa{sv}})",
 			               &secrets);
 		}
-		r->callback (r->agent, r, secrets, error, r->callback_data);
 	}
 
-	request_free (r);
+	_call_id_invoke_callback (call_id, secrets, error, FALSE, TRUE);
 }
 
+/*****************************************************************************/
+
 NMSecretAgentCallId *
 nm_secret_agent_get_secrets (NMSecretAgent *self,
                              const char *path,
@@ -367,160 +407,139 @@ nm_secret_agent_get_secrets (NMSecretAgent *self,
 {
 	NMSecretAgentPrivate *priv;
 	GVariant *dict;
-	NMSecretAgentCallId *r;
+	NMSecretAgentCallId *call_id;
 
 	g_return_val_if_fail (NM_IS_SECRET_AGENT (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (path && *path, NULL);
-	g_return_val_if_fail (setting_name != NULL, NULL);
+	g_return_val_if_fail (setting_name, NULL);
+	g_return_val_if_fail (callback, NULL);
 
 	priv = NM_SECRET_AGENT_GET_PRIVATE (self);
-	g_return_val_if_fail (priv->proxy != NULL, NULL);
 
 	dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
 
 	/* Mask off the private flags if present */
-	flags &= ~NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM;
-	flags &= ~NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS;
-
-	r = request_new (self, "GetSecrets", path, setting_name, callback, callback_data);
-	r->is_get_secrets = TRUE;
-
-	g_dbus_proxy_call (priv->proxy,
-	                   "GetSecrets",
-	                   g_variant_new ("(@a{sa{sv}}os^asu)",
-	                                  dict,
-	                                  path,
-	                                  setting_name,
-	                                  hints ?: NM_PTRARRAY_EMPTY (const char *),
-	                                  (guint32) flags),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   120000,
-	                   r->cancellable,
-	                   get_callback,
-	                   r);
-
-	g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), -1);
-
-	return r;
+	flags &= ~(  NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM
+	           | NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS);
+
+	call_id = _call_id_new (self, METHOD_GET_SECRETS, path, setting_name, callback, callback_data);
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        priv->dbus_owner,
+	                        NM_DBUS_PATH_SECRET_AGENT,
+	                        NM_DBUS_INTERFACE_SECRET_AGENT,
+	                        call_id->method_name,
+	                        g_variant_new ("(@a{sa{sv}}os^asu)",
+	                                       dict,
+	                                       path,
+	                                       setting_name,
+	                                       hints ?: NM_PTRARRAY_EMPTY (const char *),
+	                                       (guint32) flags),
+	                        G_VARIANT_TYPE ("(a{sa{sv}})"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        120000,
+	                        call_id->cancellable,
+	                        _dbus_call_cb,
+	                        call_id);
+
+	return call_id;
 }
 
 /*****************************************************************************/
 
 static void
-cancel_done (GObject *proxy, GAsyncResult *result, gpointer user_data)
+_call_cancel_cb (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
 {
-	gs_free char *description = user_data;
+	NMSecretAgentCallId *call_id = user_data;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *ret = NULL;
 
-	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error);
-	if (!ret) {
-		nm_log_dbg (LOGD_AGENTS, "%s%s%s: agent failed to cancel secrets: %s",
-		            NM_PRINT_FMT_QUOTED (description, "(", description, ")", "???"),
-		            error->message);
-	}
-}
-
-static void
-do_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId *r, gboolean disposing)
-{
-	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
-	GCancellable *cancellable;
-	NMSecretAgentCallback callback;
-	gpointer callback_data;
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
 
-	g_return_if_fail (r->agent == self);
-	g_return_if_fail (r->cancellable);
-
-	if (   r->is_get_secrets
-	    && priv->proxy) {
-		/* for GetSecrets call, we must cancel the request. */
-		g_dbus_proxy_call (G_DBUS_PROXY (priv->proxy),
-		                   "CancelGetSecrets",
-		                   g_variant_new ("(os)",
-		                                  r->path,
-		                                  r->setting_name),
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   NULL,
-		                   cancel_done,
-		                   g_strdup (nm_secret_agent_get_description (self)));
+	if (ret)
+		_LOG2T (call_id, "success cancelling GetSecrets");
+	else if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
+		_LOG2T (call_id, "cancelling GetSecrets no longer works as service disconnected");
+	else {
+		_LOG2T (call_id, "failed to cancel GetSecrets: %s",
+		        error->message);
 	}
 
-	cancellable = r->cancellable;
-	callback = r->callback;
-	callback_data = r->callback_data;
-
-	/* During g_cancellable_cancel() the d-bus method might return synchronously.
-	 * Clear r->cancellable first, so that it doesn't actually do anything.
-	 * After that, @r might be already freed. */
-	r->cancellable = NULL;
-	g_cancellable_cancel (cancellable);
-	g_object_unref (cancellable);
-
-	/* Don't free the request @r. It will be freed when the d-bus call returns.
-	 * Only clear r->cancellable to indicate that the request was cancelled. */
-
-	if (callback) {
-		gs_free_error GError *error = NULL;
-
-		nm_utils_error_set_cancelled (&error, disposing, "NMSecretAgent");
-		/* @r might be a dangling pointer at this point. However, that is no problem
-		 * to pass it as (opaque) call_id. */
-		callback (self, r, NULL, error, callback_data);
-	}
+	_call_id_free (call_id);
 }
 
 /**
- * nm_secret_agent_cancel_secrets:
- * @self: #NMSecretAgent instance
- * @call_id: the call id to cancel
+ * nm_secret_agent_cancel_call:
+ * @self: the #NMSecretAgent instance for the @call_id.
+ *   Maybe be %NULL if @call_id is %NULL.
+ * @call_id: (allow-none): the call id to cancel. May be %NULL for convenience,
+ *   in which case it does nothing.
  *
  * It is an error to pass an invalid @call_id or a @call_id for an operation
- * that already completed. NMSecretAgent will always invoke the callback,
- * also for cancel() and dispose().
- * In case of nm_secret_agent_cancel_secrets() this will synchronously invoke the
- * callback before nm_secret_agent_cancel_secrets() returns.
+ * that already completed. It is also an error to cancel the call from inside
+ * the callback, at that point the call is already completed.
+ * In case of nm_secret_agent_cancel_call() this will synchronously invoke the
+ * callback before nm_secret_agent_cancel_call() returns.
  */
 void
-nm_secret_agent_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId *call_id)
+nm_secret_agent_cancel_call (NMSecretAgent *self,
+                             NMSecretAgentCallId *call_id)
 {
-	NMSecretAgentCallId *r = call_id;
-
-	g_return_if_fail (NM_IS_SECRET_AGENT (self));
-	g_return_if_fail (r);
-
-	nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests,
-	                            &r->lst));
-
-	c_list_unlink (&r->lst);
+	NMSecretAgentPrivate *priv;
+	gboolean free_call_id = TRUE;
 
-	do_cancel_secrets (self, r, FALSE);
-}
+	if (!call_id) {
+		/* for convenience, %NULL is accepted fine. */
+		nm_assert (!self || NM_IS_SECRET_AGENT (self));
+		return;
+	}
 
-/*****************************************************************************/
+	g_return_if_fail (NM_IS_SECRET_AGENT (call_id->self));
+	g_return_if_fail (!c_list_is_empty (&call_id->lst));
 
-static void
-agent_save_cb (GObject *proxy,
-               GAsyncResult *result,
-               gpointer user_data)
-{
-	NMSecretAgentCallId *r = user_data;
+	/* Theoretically, call-id already has a self pointer. But nm_secret_agent_cancel_call() has only
+	 * one user: NMAgentManager. And that one has the self-pointer at hand, so the only purpose of
+	 * the @self argument is to assert that we are cancelling the expected call.
+	 *
+	 * We could drop the @self argument, but that just remove an additional assert-check from
+	 * our code, without making a simplification for the only caller of this function. */
+	g_return_if_fail (self == call_id->self);
 
-	if (request_check_return (r)) {
-		gs_free_error GError *error = NULL;
-		gs_unref_variant GVariant *ret = NULL;
+	priv = NM_SECRET_AGENT_GET_PRIVATE (self);
 
-		ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error);
-		if (!ret)
-			g_dbus_error_strip_remote_error (error);
-		r->callback (r->agent, r, NULL, error, r->callback_data);
+	nm_assert (c_list_contains (&priv->requests,
+	                            &call_id->lst));
+
+	nm_clear_g_cancellable (&call_id->cancellable);
+
+	if (nm_streq (call_id->method_name, METHOD_GET_SECRETS)) {
+		g_dbus_connection_call (priv->dbus_connection,
+		                        priv->dbus_owner,
+		                        NM_DBUS_PATH_SECRET_AGENT,
+		                        NM_DBUS_INTERFACE_SECRET_AGENT,
+		                        METHOD_CANCEL_GET_SECRETS,
+		                        g_variant_new ("(os)",
+		                                       call_id->path,
+		                                       call_id->setting_name),
+		                        G_VARIANT_TYPE ("()"),
+		                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                        NM_SHUTDOWN_TIMEOUT_MS,
+		                        NULL, /* this operation is not cancellable. We rely on the timeout. */
+		                        _call_cancel_cb,
+		                        call_id);
+		/* we keep call-id alive, but it will be unlinked from priv->requests.
+		 * _call_cancel_cb() will finally free it later. */
+		free_call_id = FALSE;
 	}
 
-	request_free (r);
+	_call_id_invoke_callback (call_id, NULL, NULL, TRUE, free_call_id);
 }
 
+/*****************************************************************************/
+
 NMSecretAgentCallId *
 nm_secret_agent_save_secrets (NMSecretAgent *self,
                               const char *path,
@@ -530,7 +549,7 @@ nm_secret_agent_save_secrets (NMSecretAgent *self,
 {
 	NMSecretAgentPrivate *priv;
 	GVariant *dict;
-	NMSecretAgentCallId *r;
+	NMSecretAgentCallId *call_id;
 
 	g_return_val_if_fail (NM_IS_SECRET_AGENT (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -541,43 +560,28 @@ nm_secret_agent_save_secrets (NMSecretAgent *self,
 	/* Caller should have ensured that only agent-owned secrets exist in 'connection' */
 	dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
 
-	r = request_new (self, "SaveSecrets", path, NULL, callback, callback_data);
-	g_dbus_proxy_call (priv->proxy,
-	                   "SaveSecrets",
-	                   g_variant_new ("(@a{sa{sv}}o)",
-	                                  dict,
-	                                  path),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   NULL, /* cancelling the request does *not* cancel the D-Bus call. */
-	                   agent_save_cb,
-	                   r);
-
-	return r;
+	call_id = _call_id_new (self, METHOD_SAVE_SECRETS, path, NULL, callback, callback_data);
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        priv->dbus_owner,
+	                        NM_DBUS_PATH_SECRET_AGENT,
+	                        NM_DBUS_INTERFACE_SECRET_AGENT,
+	                        call_id->method_name,
+	                        g_variant_new ("(@a{sa{sv}}o)",
+	                                       dict,
+	                                       path),
+	                        G_VARIANT_TYPE ("()"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        60000,
+	                        call_id->cancellable,
+	                        _dbus_call_cb,
+	                        call_id);
+
+	return call_id;
 }
 
 /*****************************************************************************/
 
-static void
-agent_delete_cb (GObject *proxy,
-                 GAsyncResult *result,
-                 gpointer user_data)
-{
-	NMSecretAgentCallId *r = user_data;
-
-	if (request_check_return (r)) {
-		gs_free_error GError *error = NULL;
-		gs_unref_variant GVariant *ret = NULL;
-
-		ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error);
-		if (!ret)
-			g_dbus_error_strip_remote_error (error);
-		r->callback (r->agent, r, NULL, error, r->callback_data);
-	}
-
-	request_free (r);
-}
-
 NMSecretAgentCallId *
 nm_secret_agent_delete_secrets (NMSecretAgent *self,
                                 const char *path,
@@ -587,7 +591,7 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self,
 {
 	NMSecretAgentPrivate *priv;
 	GVariant *dict;
-	NMSecretAgentCallId *r;
+	NMSecretAgentCallId *call_id;
 
 	g_return_val_if_fail (NM_IS_SECRET_AGENT (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -598,81 +602,90 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self,
 	/* No secrets sent; agents must be smart enough to track secrets using the UUID or something */
 	dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
 
-	r = request_new (self, "DeleteSecrets", path, NULL, callback, callback_data);
-	g_dbus_proxy_call (priv->proxy,
-	                   "DeleteSecrets",
-	                   g_variant_new ("(@a{sa{sv}}o)",
-	                                  dict,
-	                                  path),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   NULL, /* cancelling the request does *not* cancel the D-Bus call. */
-	                   agent_delete_cb,
-	                   r);
-	return r;
+	call_id = _call_id_new (self, METHOD_DELETE_SECRETS, path, NULL, callback, callback_data);
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        priv->dbus_owner,
+	                        NM_DBUS_PATH_SECRET_AGENT,
+	                        NM_DBUS_INTERFACE_SECRET_AGENT,
+	                        call_id->method_name,
+	                        g_variant_new ("(@a{sa{sv}}o)",
+	                                       dict,
+	                                       path),
+	                        G_VARIANT_TYPE ("()"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        60000,
+	                        call_id->cancellable,
+	                        _dbus_call_cb,
+	                        call_id);
+	return call_id;
 }
 
 /*****************************************************************************/
 
 static void
-_on_disconnected_cleanup (NMSecretAgentPrivate *priv)
+name_owner_changed (NMSecretAgent *self,
+                    const char *owner)
 {
-	if (priv->connection_is_private) {
-		nm_clear_g_signal_handler (priv->bus_mgr,
-		                           &priv->on_disconnected_id.obj_signal);
-	} else {
-		nm_clear_g_dbus_connection_signal (priv->connection,
-		                                   &priv->on_disconnected_id.dbus_signal);
-	}
+	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
 
-	g_clear_object (&priv->connection);
-	g_clear_object (&priv->proxy);
-	g_clear_object (&priv->bus_mgr);
-}
+	nm_assert (!priv->name_owner_cancellable);
 
-static void
-_on_disconnected_private_connection (NMDBusManager *mgr,
-                                     GDBusConnection *connection,
-                                     NMSecretAgent *self)
-{
-	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+	owner = nm_str_not_empty (owner);
+
+	_LOGT ("name-owner-changed: %s%s%s",
+	       NM_PRINT_FMT_QUOTED (owner, "has ", owner, "", "disconnected"));
 
-	if (priv->connection != connection)
+	if (owner)
 		return;
 
-	_LOGt ("private connection disconnected");
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->name_owner_changed_id);
 
-	_on_disconnected_cleanup (priv);
 	g_signal_emit (self, signals[DISCONNECTED], 0);
 }
 
 static void
-_on_disconnected_name_owner_changed (GDBusConnection *connection,
-                                     const char       *sender_name,
-                                     const char       *object_path,
-                                     const char       *interface_name,
-                                     const char       *signal_name,
-                                     GVariant         *parameters,
-                                     gpointer          user_data)
+name_owner_changed_cb (GDBusConnection *dbus_connection,
+                       const char       *sender_name,
+                       const char       *object_path,
+                       const char       *interface_name,
+                       const char       *signal_name,
+                       GVariant         *parameters,
+                       gpointer          user_data)
 {
 	NMSecretAgent *self = NM_SECRET_AGENT (user_data);
-	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
-	const char *old_owner = NULL, *new_owner = NULL;
+	const char *new_owner = NULL;
+
+	if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) {
+		g_variant_get (parameters,
+		               "(&s&s&s)",
+		               NULL,
+		               NULL,
+		               &new_owner);
+	}
 
-	g_variant_get (parameters,
-	               "(&s&s&s)",
-	               NULL,
-	               &old_owner,
-	               &new_owner);
+	nm_clear_g_cancellable (&NM_SECRET_AGENT_GET_PRIVATE (self)->name_owner_cancellable);
 
-	_LOGt ("name-owner-changed: %s%s%s => %s%s%s",
-	       NM_PRINT_FMT_QUOTE_STRING (old_owner),
-	       NM_PRINT_FMT_QUOTE_STRING (new_owner));
+	name_owner_changed (self, new_owner);
+}
 
-	if (!*new_owner) {
-		_on_disconnected_cleanup (priv);
-		g_signal_emit (self, signals[DISCONNECTED], 0);
-	}
+static void
+get_name_owner_cb (const char *name_owner,
+                   GError *error,
+                   gpointer user_data)
+{
+	NMSecretAgent *self;
+
+	if (   !name_owner
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+
+	g_clear_object (&NM_SECRET_AGENT_GET_PRIVATE (self)->name_owner_cancellable);
+
+	name_owner_changed (self, name_owner);
 }
 
 /*****************************************************************************/
@@ -692,16 +705,16 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 	char buf_subject[64];
 	char buf_caps[150];
 	gulong uid;
-	GDBusConnection *connection;
+	GDBusConnection *dbus_connection;
 
 	g_return_val_if_fail (context != NULL, NULL);
 	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
 	g_return_val_if_fail (nm_auth_subject_is_unix_process (subject), NULL);
 	g_return_val_if_fail (identifier != NULL, NULL);
 
-	connection = g_dbus_method_invocation_get_connection (context);
+	dbus_connection = g_dbus_method_invocation_get_connection (context);
 
-	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (G_IS_DBUS_CONNECTION (dbus_connection), NULL);
 
 	uid = nm_auth_subject_get_unix_process_uid (subject);
 
@@ -715,16 +728,13 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 
 	priv = NM_SECRET_AGENT_GET_PRIVATE (self);
 
-	priv->bus_mgr = g_object_ref (nm_dbus_manager_get ());
-	priv->connection = g_object_ref (connection);
-	priv->connection_is_private = !!nm_dbus_manager_connection_get_private_name (priv->bus_mgr, connection);
+	priv->dbus_connection = g_object_ref (dbus_connection);
 
-	_LOGt ("constructed: %s, owner=%s%s%s (%s), private-connection=%d, unique-name=%s%s%s, capabilities=%s",
+	_LOGT ("constructed: %s, owner=%s%s%s (%s), unique-name=%s%s%s, capabilities=%s",
 	       (description = _create_description (dbus_owner, identifier, uid)),
 	       NM_PRINT_FMT_QUOTE_STRING (owner_username),
 	       nm_auth_subject_to_string (subject, buf_subject, sizeof (buf_subject)),
-	       priv->connection_is_private,
-	       NM_PRINT_FMT_QUOTE_STRING (g_dbus_connection_get_unique_name (priv->connection)),
+	       NM_PRINT_FMT_QUOTE_STRING (g_dbus_connection_get_unique_name (priv->dbus_connection)),
 	       _capabilities_to_string (capabilities, buf_caps, sizeof (buf_caps)));
 
 	priv->identifier = g_strdup (identifier);
@@ -734,27 +744,19 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 	priv->capabilities = capabilities;
 	priv->subject = g_object_ref (subject);
 
-	priv->proxy = nm_dbus_manager_new_proxy (priv->bus_mgr,
-	                                         priv->connection,
-	                                         G_TYPE_DBUS_PROXY,
-	                                         priv->dbus_owner,
-	                                         NM_DBUS_PATH_SECRET_AGENT,
-	                                         NM_DBUS_INTERFACE_SECRET_AGENT);
-
-	/* we cannot subscribe to notify::g-name-owner because that doesn't work
-	 * for unique names and it doesn't work for private connections. */
-	if (priv->connection_is_private) {
-		priv->on_disconnected_id.obj_signal = g_signal_connect (priv->bus_mgr,
-		                                                        NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED,
-		                                                        G_CALLBACK (_on_disconnected_private_connection),
-		                                                        self);
-	} else {
-		priv->on_disconnected_id.dbus_signal = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->connection,
-		                                                                                               priv->dbus_owner,
-		                                                                                               _on_disconnected_name_owner_changed,
-		                                                                                               self,
-		                                                                                               NULL);
-	}
+	priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+	                                                                                      priv->dbus_owner,
+	                                                                                      name_owner_changed_cb,
+	                                                                                      self,
+	                                                                                      NULL);
+
+	priv->name_owner_cancellable = g_cancellable_new ();
+	nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+	                                        priv->dbus_owner,
+	                                        -1,
+	                                        priv->name_owner_cancellable,
+	                                        get_name_owner_cb,
+	                                        self);
 
 	return self;
 }
@@ -764,6 +766,7 @@ nm_secret_agent_init (NMSecretAgent *self)
 {
 	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
 
+	c_list_init (&priv->permissions);
 	c_list_init (&priv->requests);
 }
 
@@ -772,18 +775,13 @@ dispose (GObject *object)
 {
 	NMSecretAgent *self = NM_SECRET_AGENT (object);
 	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
-	CList *iter;
 
-again:
-	c_list_for_each (iter, &priv->requests) {
-		c_list_unlink (iter);
-		do_cancel_secrets (self, c_list_entry (iter, NMSecretAgentCallId, lst), TRUE);
-		goto again;
-	}
+	nm_assert (c_list_is_empty (&priv->requests));
 
-	_on_disconnected_cleanup (priv);
+	nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+	                                   &priv->name_owner_changed_id);
 
-	g_clear_object (&priv->subject);
+	nm_clear_g_cancellable (&priv->name_owner_cancellable);
 
 	G_OBJECT_CLASS (nm_secret_agent_parent_class)->dispose (object);
 }
@@ -799,11 +797,15 @@ finalize (GObject *object)
 	g_free (priv->owner_username);
 	g_free (priv->dbus_owner);
 
-	g_slist_free_full (priv->permissions, g_free);
+	nm_c_list_elem_free_all (&priv->permissions, g_free);
+
+	g_clear_object (&priv->subject);
+
+	g_clear_object (&priv->dbus_connection);
 
 	G_OBJECT_CLASS (nm_secret_agent_parent_class)->finalize (object);
 
-	_LOGt ("finalized");
+	_LOGT ("finalized");
 }
 
 static void
@@ -823,4 +825,3 @@ nm_secret_agent_class_init (NMSecretAgentClass *config_class)
 	                  g_cclosure_marshal_VOID__VOID,
 	                  G_TYPE_NONE, 0);
 }
-
diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h
index 209a1009..ea86e432 100644
--- a/src/settings/nm-secret-agent.h
+++ b/src/settings/nm-secret-agent.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 - 2011 Red Hat, Inc.
  */
 
@@ -79,9 +65,6 @@ NMSecretAgentCallId *nm_secret_agent_get_secrets (NMSecretAgent *agent,
                                                   NMSecretAgentCallback callback,
                                                   gpointer callback_data);
 
-void        nm_secret_agent_cancel_secrets (NMSecretAgent *agent,
-                                            NMSecretAgentCallId *call_id);
-
 NMSecretAgentCallId *nm_secret_agent_save_secrets (NMSecretAgent *agent,
                                                    const char *path,
                                                    NMConnection *connection,
@@ -94,4 +77,7 @@ NMSecretAgentCallId *nm_secret_agent_delete_secrets (NMSecretAgent *agent,
                                                      NMSecretAgentCallback callback,
                                                      gpointer callback_data);
 
+void nm_secret_agent_cancel_call (NMSecretAgent *self,
+                                  NMSecretAgentCallId *call_id);
+
 #endif /* __NETWORKMANAGER_SECRET_AGENT_H__ */
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index ed0cb8e8..ccbab807 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -1,21 +1,7 @@
-/* NetworkManager system settings service
- *
- * 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 2008 Novell, Inc.
- * Copyright 2008 - 2014 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2008 - 2014 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index 61d5c246..dfc3786c 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -1,21 +1,7 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 Novell, Inc.
- * (C) Copyright 2008 - 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2008 - 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_SETTINGS_CONNECTION_H__
diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c
index 09010931..630532ef 100644
--- a/src/settings/nm-settings-plugin.c
+++ b/src/settings/nm-settings-plugin.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2018 Red Hat, Inc.
  * Copyright (C) 2008 Novell, Inc.
  */
diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h
index 4df3472e..d27429db 100644
--- a/src/settings/nm-settings-plugin.h
+++ b/src/settings/nm-settings-plugin.h
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2018 Red Hat, Inc.
  * Copyright (C) 2008 Novell, Inc.
  */
diff --git a/src/settings/nm-settings-storage.c b/src/settings/nm-settings-storage.c
index 935b5b48..483773f6 100644
--- a/src/settings/nm-settings-storage.c
+++ b/src/settings/nm-settings-storage.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * 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 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/settings/nm-settings-storage.h b/src/settings/nm-settings-storage.h
index c43145b5..62ee8876 100644
--- a/src/settings/nm-settings-storage.h
+++ b/src/settings/nm-settings-storage.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * 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 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_SETTINGS_STORAGE_H__
diff --git a/src/settings/nm-settings-utils.c b/src/settings/nm-settings-utils.c
index 0d636537..c5ec6c31 100644
--- a/src/settings/nm-settings-utils.c
+++ b/src/settings/nm-settings-utils.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2019 Red Hat, Inc.
  */
 
diff --git a/src/settings/nm-settings-utils.h b/src/settings/nm-settings-utils.h
index a2a22dc4..1f9a6ea1 100644
--- a/src/settings/nm-settings-utils.h
+++ b/src/settings/nm-settings-utils.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * 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 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_SETTINGS_UTILS_H__
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 6529cc58..f964fb16 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -1,25 +1,10 @@
-/* NetworkManager system settings service
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Søren Sandmann <sandmann@daimi.au.dk>
  * Dan Williams <dcbw@redhat.com>
  * Tambet Ingo <tambet@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2007 - 2011 Red Hat, Inc.
- * (C) Copyright 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2011 Red Hat, Inc.
+ * Copyright (C) 2008 Novell, Inc.
  */
 
 #include "nm-default.h"
@@ -2487,17 +2472,6 @@ nm_settings_add_connection_dbus (NMSettings *self,
 		goto done;
 	}
 
-	/* FIXME: 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
-	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
-	 */
-	if (nm_utils_connection_is_adhoc_wpa (connection)) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		                             "WPA Ad-Hoc disabled due to kernel bugs");
-		goto done;
-	}
-
 	if (!nm_auth_is_subject_in_acl_set_error (connection,
 	                                          subject,
 	                                          NM_SETTINGS_ERROR,
@@ -2776,9 +2750,10 @@ impl_settings_load_connections (NMDBusObject *obj,
 		for (i = 0; i < n_entries; i++) {
 			NMSettingsPluginConnectionLoadEntry *entry = &entries[i];
 
-			if (!entry->handled)
+			if (!entry->handled) {
 				_LOGW ("load: no settings plugin could load \"%s\"", entry->filename);
-			else if (entry->error) {
+				nm_assert (!entry->error);
+			} else if (entry->error) {
 				_LOGW ("load: failure to load \"%s\": %s", entry->filename, entry->error->message);
 				g_clear_error (&entry->error);
 			} else
@@ -2847,6 +2822,7 @@ impl_settings_reload_connections (NMDBusObject *obj,
 
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONNS_RELOAD, NULL, TRUE, NULL, invocation, NULL);
 
+	/* We MUST return %TRUE here, otherwise older libnm versions might misbehave. */
 	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
 }
 
@@ -3140,9 +3116,9 @@ add_plugin (NMSettings *self,
 
 	priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));
 
-	nm_shutdown_wait_obj_register_full (G_OBJECT (plugin),
-	                                    g_strdup_printf ("%s-settings-plugin", pname),
-	                                    TRUE);
+	nm_shutdown_wait_obj_register_object_full (plugin,
+	                                           g_strdup_printf ("%s-settings-plugin", pname),
+	                                           TRUE);
 
 	_LOGI ("Loaded settings plugin: %s (%s%s%s)",
 	       pname,
@@ -3455,14 +3431,27 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 	 */
 	if (   !NM_DEVICE_GET_CLASS (device)->new_default_connection
 	    || !nm_device_get_managed (device, FALSE)
-	    || g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ())
-	    || have_connection_for_device (self, device)
-	    || nm_config_get_no_auto_default_for_device (priv->config, device))
+	    || g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()))
 		return;
 
+	if (nm_config_get_no_auto_default_for_device (priv->config, device)) {
+		_LOGT ("auto-default: cannot create auto-default connection for device %s: disabled by \"no-auto-default\"",
+		       nm_device_get_iface (device));
+		return;
+	}
+
+	if (have_connection_for_device (self, device)) {
+		_LOGT ("auto-default: cannot create auto-default connection for device %s: already has a profile",
+		       nm_device_get_iface (device));
+		return;
+	}
+
 	connection = nm_device_new_default_connection (device);
-	if (!connection)
+	if (!connection) {
+		_LOGT ("auto-default: cannot create auto-default connection for device %s",
+		       nm_device_get_iface (device));
 		return;
+	}
 
 	_LOGT ("auto-default: creating in-memory connection %s (%s) for device %s",
 	       nm_connection_get_uuid (connection),
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index d2bf72d6..aa7e36e0 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -1,25 +1,10 @@
-/* NetworkManager system settings service
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Søren Sandmann <sandmann@daimi.au.dk>
  * Dan Williams <dcbw@redhat.com>
  * Tambet Ingo <tambet@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2007 - 2011 Red Hat, Inc.
- * (C) Copyright 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2011 Red Hat, Inc.
+ * Copyright (C) 2008 Novell, Inc.
  */
 
 #ifndef __NM_SETTINGS_H__
diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build
index 58acdcfc..d9dd3edb 100644
--- a/src/settings/plugins/ifcfg-rh/meson.build
+++ b/src/settings/plugins/ifcfg-rh/meson.build
@@ -25,23 +25,24 @@ core_sources = files(
   'shvar.c',
 )
 
-deps = [
-  nm_dep,
-]
-
 libnms_ifcfg_rh_core = static_library(
   'nms-ifcfg-rh-core',
   sources: core_sources,
-  dependencies: deps,
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
 )
 
-sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-storage.c', 'nms-ifcfg-rh-plugin.c')
+sources = [dbus_sources] + core_sources + files(
+  'nms-ifcfg-rh-storage.c',
+  'nms-ifcfg-rh-plugin.c',
+)
 
 libnm_settings_plugin_ifcfg_rh = shared_module(
   'nm-settings-plugin-ifcfg-rh',
   sources: sources,
-  dependencies: deps,
-  link_with: [libnms_ifcfg_rh_core],
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
+  link_with: libnms_ifcfg_rh_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
   install: true,
@@ -50,27 +51,17 @@ libnm_settings_plugin_ifcfg_rh = shared_module(
 
 core_plugins += libnm_settings_plugin_ifcfg_rh
 
-# FIXME: check_so_symbols replacement
-'''
-run_target(
-  'check-local-symbols-settings-ifcfg-rh',
-  command: [check_so_symbols, libnm_settings_plugin_ifcfg_rh.full_path()],
-  depends: libnm_settings_plugin_ifcfg_rh,
-)
-
-check-local-symbols-settings-ifcfg-rh: src/settings/plugins/ifcfg-rh/libnm-settings-plugin-ifcfg-rh.la
-  $(call check_so_symbols,$(builddir)/src/settings/plugins/ifcfg-rh/.libs/libnm-settings-plugin-ifcfg-rh.so)
-'''
+data = [
+  'nm-ifdown',
+  'nm-ifup',
+]
 
 install_data(
-  ['nm-ifup', 'nm-ifdown'],
+  data,
   install_dir: nm_libexecdir,
   install_mode: 'rwxr-xr-x',
 )
 
-meson.add_install_script('sh', '-c',
-                         'mkdir -p $DESTDIR/@0@/sysconfig/network-scripts'.format(nm_sysconfdir))
-
 if enable_tests
   subdir('tests')
 endif
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h
index ff01fc7a..6a01a5ac 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h
@@ -1,20 +1,6 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 - 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2013 Red Hat, Inc.
  */
 
 #ifndef __COMMON_H__
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index cc4fe4ce..f57ca1a7 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -1,22 +1,7 @@
-/* NetworkManager system settings service
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Dan Williams <dcbw@redhat.com>
  * Søren Sandmann <sandmann@daimi.au.dk>
- *
- * 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) 2007 - 2011 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
index 1db36083..14cadac6 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
@@ -1,22 +1,7 @@
-/* NetworkManager system settings service
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Dan Williams <dcbw@redhat.com>
  * Søren Sandmann <sandmann@daimi.au.dk>
- *
- * 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) 2007 - 2008 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 68ebd781..a1d3236e 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -1,20 +1,6 @@
-/* NetworkManager system settings service
- *
- * 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 2008 - 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -29,6 +15,7 @@
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
+#include <linux/rtnetlink.h>
 
 #include "nm-glib-aux/nm-secret-utils.h"
 #include "nm-connection.h"
@@ -753,25 +740,36 @@ parse_route_line_is_comment (const char *line)
 
 /*****************************************************************************/
 
+typedef enum {
+	PARSE_LINE_AF_FLAG_FOR_IPV4 = 0x01,
+	PARSE_LINE_AF_FLAG_FOR_IPV6 = 0x02,
+} ParseLineAFFlag;
+
 typedef struct {
 	const char *key;
 
 	/* the element is not available in this case. */
-	bool disabled:1;
+	ParseLineAFFlag disabled:3;
+
+	bool disabled_with_options_route:1;
 
 	/* whether the element is to be ignored. Ignord is different from
 	 * "disabled", because we still parse the option, but don't use it. */
-	bool ignore:1;
+	ParseLineAFFlag ignore:3;
 
 	bool int_base_16:1;
 
+	/* the type, one of PARSE_LINE_TYPE_* */
+	char type;
+
+} ParseLineInfo;
+
+typedef struct {
+
 	/* whether the command line option was found, and @v is
 	 * initialized. */
 	bool has:1;
 
-	/* the type, one of PARSE_LINE_TYPE_* */
-	char type;
-
 	union {
 		guint8 uint8;
 		guint32 uint32;
@@ -786,7 +784,7 @@ typedef struct {
 		} addr;
 	} v;
 
-} ParseLineInfo;
+} ParseLineData;
 
 enum {
 	/* route attributes */
@@ -794,6 +792,7 @@ enum {
 	PARSE_LINE_ATTR_ROUTE_SRC,
 	PARSE_LINE_ATTR_ROUTE_FROM,
 	PARSE_LINE_ATTR_ROUTE_TOS,
+	PARSE_LINE_ATTR_ROUTE_SCOPE,
 	PARSE_LINE_ATTR_ROUTE_ONLINK,
 	PARSE_LINE_ATTR_ROUTE_WINDOW,
 	PARSE_LINE_ATTR_ROUTE_CWND,
@@ -817,6 +816,7 @@ enum {
 #define PARSE_LINE_TYPE_ADDR_WITH_PREFIX  'p'
 #define PARSE_LINE_TYPE_IFNAME            'i'
 #define PARSE_LINE_TYPE_FLAG              'f'
+#define PARSE_LINE_TYPE_ROUTE_SCOPE       'S'
 
 /**
  * parse_route_line:
@@ -847,29 +847,23 @@ parse_route_line (const char *line,
                   NMIPRoute **out_route,
                   GError **error)
 {
-	nm_auto_unref_ip_route NMIPRoute *route = NULL;
-	gs_free const char **words_free = NULL;
-	const char *const*words;
-	const char *s;
-	gsize i_words;
-	guint i;
-	char buf1[256];
-	char buf2[256];
-	ParseLineInfo infos[] = {
+	static const ParseLineInfo parse_infos[] = {
 		[PARSE_LINE_ATTR_ROUTE_TABLE]     = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE,
 		                                      .type = PARSE_LINE_TYPE_UINT32, },
 		[PARSE_LINE_ATTR_ROUTE_SRC]       = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC,
 		                                      .type = PARSE_LINE_TYPE_ADDR, },
 		[PARSE_LINE_ATTR_ROUTE_FROM]      = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM,
 		                                      .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
-		                                      .disabled = (addr_family != AF_INET6), },
+		                                      .disabled = PARSE_LINE_AF_FLAG_FOR_IPV4, },
 		[PARSE_LINE_ATTR_ROUTE_TOS]       = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS,
 		                                      .type = PARSE_LINE_TYPE_UINT8,
 		                                      .int_base_16 = TRUE,
-		                                      .ignore = (addr_family != AF_INET), },
+		                                      .ignore = PARSE_LINE_AF_FLAG_FOR_IPV6, },
+		[PARSE_LINE_ATTR_ROUTE_SCOPE]     = { .key = NM_IP_ROUTE_ATTRIBUTE_SCOPE,
+		                                      .type = PARSE_LINE_TYPE_ROUTE_SCOPE,
+		                                      .ignore = PARSE_LINE_AF_FLAG_FOR_IPV6, },
 		[PARSE_LINE_ATTR_ROUTE_ONLINK]    = { .key = NM_IP_ROUTE_ATTRIBUTE_ONLINK,
-		                                      .type = PARSE_LINE_TYPE_FLAG,
-		                                      .ignore = (addr_family != AF_INET), },
+		                                      .type = PARSE_LINE_TYPE_FLAG, },
 		[PARSE_LINE_ATTR_ROUTE_WINDOW]    = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW,
 		                                      .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
 		[PARSE_LINE_ATTR_ROUTE_CWND]      = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND,
@@ -883,19 +877,31 @@ parse_route_line (const char *line,
 
 		[PARSE_LINE_ATTR_ROUTE_TO]        = { .key = "to",
 		                                      .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
-		                                      .disabled = (options_route != NULL), },
+		                                      .disabled_with_options_route = TRUE, },
 		[PARSE_LINE_ATTR_ROUTE_VIA]       = { .key = "via",
 		                                      .type = PARSE_LINE_TYPE_ADDR,
-		                                      .disabled = (options_route != NULL), },
+		                                      .disabled_with_options_route = TRUE, },
 		[PARSE_LINE_ATTR_ROUTE_METRIC]    = { .key = "metric",
 		                                      .type = PARSE_LINE_TYPE_UINT32,
-		                                      .disabled = (options_route != NULL), },
+		                                      .disabled_with_options_route = TRUE, },
 
 		[PARSE_LINE_ATTR_ROUTE_DEV]       = { .key = "dev",
 		                                      .type = PARSE_LINE_TYPE_IFNAME,
-		                                      .ignore = TRUE,
-		                                      .disabled = (options_route != NULL), },
+		                                      .ignore = PARSE_LINE_AF_FLAG_FOR_IPV4 | PARSE_LINE_AF_FLAG_FOR_IPV6,
+		                                      .disabled_with_options_route = TRUE, },
 	};
+	nm_auto_unref_ip_route NMIPRoute *route = NULL;
+	gs_free const char **words_free = NULL;
+	const char *const*words;
+	const char *s;
+	gsize i_words;
+	guint i;
+	char buf1[256];
+	char buf2[256];
+	ParseLineData parse_datas[G_N_ELEMENTS (parse_infos)] = { };
+	const ParseLineAFFlag af_flag =   (addr_family == AF_INET)
+	                                ? PARSE_LINE_AF_FLAG_FOR_IPV4
+	                                : PARSE_LINE_AF_FLAG_FOR_IPV6;
 
 	nm_assert (line);
 	nm_assert_addr_family (addr_family);
@@ -923,19 +929,22 @@ parse_route_line (const char *line,
 	for (i_words = 0; words[i_words]; ) {
 		const gsize i_words0 = i_words;
 		const char *const w = words[i_words0];
-		ParseLineInfo *info;
+		const ParseLineInfo *p_info;
+		ParseLineData *p_data;
 		gboolean unqualified_addr = FALSE;
 
-		for (i = 0; i < G_N_ELEMENTS (infos); i++) {
-			info = &infos[i];
+		for (i = 0; i < G_N_ELEMENTS (parse_infos); i++) {
+			p_info = &parse_infos[i];
+			p_data = &parse_datas[i];
 
-			if (info->disabled)
+			if (   (p_info->disabled & af_flag)
+			    || (p_info->disabled_with_options_route && options_route))
 				continue;
 
-			if (!nm_streq (w, info->key))
+			if (!nm_streq (w, p_info->key))
 				continue;
 
-			if (info->has) {
+			if (p_data->has) {
 				/* iproute2 for most arguments allows specifying them multiple times.
 				 * Let's not do that. */
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -943,8 +952,8 @@ parse_route_line (const char *line,
 				return -EINVAL;
 			}
 
-			info->has = TRUE;
-			switch (info->type) {
+			p_data->has = TRUE;
+			switch (p_info->type) {
 			case PARSE_LINE_TYPE_UINT8:
 				i_words++;
 				goto parse_line_type_uint8;
@@ -966,16 +975,22 @@ parse_route_line (const char *line,
 			case PARSE_LINE_TYPE_FLAG:
 				i_words++;
 				goto next;
+			case PARSE_LINE_TYPE_ROUTE_SCOPE:
+				i_words++;
+				goto parse_line_type_route_scope;
 			default:
 				nm_assert_not_reached ();
 			}
 		}
 
 		/* "to" is also accepted unqualified... (once) */
-		info = &infos[PARSE_LINE_ATTR_ROUTE_TO];
-		if (!info->has && !info->disabled) {
+		p_info = &parse_infos[PARSE_LINE_ATTR_ROUTE_TO];
+		p_data = &parse_datas[PARSE_LINE_ATTR_ROUTE_TO];
+		if (   !p_data->has
+		    && !(p_info->disabled & af_flag)
+		    && !(p_info->disabled_with_options_route && options_route)) {
 			unqualified_addr = TRUE;
-			info->has = TRUE;
+			p_data->has = TRUE;
 			goto parse_line_type_addr;
 		}
 
@@ -983,15 +998,44 @@ parse_route_line (const char *line,
 		             "Unrecognized argument (\"to\" is duplicate or \"%s\" is garbage)", w);
 		return -EINVAL;
 
+parse_line_type_route_scope:
+		s = words[i_words];
+		if (!s)
+			goto err_word_missing_argument;
+		if (nm_streq (s, "global"))
+			p_data->v.uint8 = RT_SCOPE_UNIVERSE;
+		else if (nm_streq (s, "nowhere"))
+			p_data->v.uint8 = RT_SCOPE_NOWHERE;
+		else if (nm_streq (s, "host"))
+			p_data->v.uint8 = RT_SCOPE_HOST;
+		else if (nm_streq (s, "link"))
+			p_data->v.uint8 = RT_SCOPE_LINK;
+		else if (nm_streq (s, "site"))
+			p_data->v.uint8 = RT_SCOPE_SITE;
+		else {
+			p_data->v.uint8 = _nm_utils_ascii_str_to_int64 (s,
+			                                                0,
+			                                                0,
+			                                                G_MAXUINT8,
+			                                                0);;
+			if (errno) {
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Argument for \"%s\" is not a valid number", w);
+				return -EINVAL;
+			}
+		}
+		i_words++;
+		goto next;
+
 parse_line_type_uint8:
 		s = words[i_words];
 		if (!s)
 			goto err_word_missing_argument;
-		info->v.uint8 = _nm_utils_ascii_str_to_int64 (s,
-		                                              info->int_base_16 ? 16 : 10,
-		                                              0,
-		                                              G_MAXUINT8,
-		                                              0);;
+		p_data->v.uint8 = _nm_utils_ascii_str_to_int64 (s,
+		                                                p_info->int_base_16 ? 16 : 10,
+		                                                0,
+		                                                G_MAXUINT8,
+		                                                0);;
 		if (errno) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Argument for \"%s\" is not a valid number", w);
@@ -1005,17 +1049,17 @@ parse_line_type_uint32_with_lock:
 		s = words[i_words];
 		if (!s)
 			goto err_word_missing_argument;
-		if (info->type == PARSE_LINE_TYPE_UINT32_WITH_LOCK) {
+		if (p_info->type == PARSE_LINE_TYPE_UINT32_WITH_LOCK) {
 			if (nm_streq (s, "lock")) {
 				s = words[++i_words];
 				if (!s)
 					goto err_word_missing_argument;
-				info->v.uint32_with_lock.lock = TRUE;
+				p_data->v.uint32_with_lock.lock = TRUE;
 			} else
-				info->v.uint32_with_lock.lock = FALSE;
-			info->v.uint32_with_lock.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);;
+				p_data->v.uint32_with_lock.lock = FALSE;
+			p_data->v.uint32_with_lock.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);;
 		} else {
-			info->v.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);
+			p_data->v.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);
 		}
 		if (errno) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -1040,16 +1084,16 @@ parse_line_type_addr_with_prefix:
 		{
 			int prefix = -1;
 
-			if (info->type == PARSE_LINE_TYPE_ADDR) {
+			if (p_info->type == PARSE_LINE_TYPE_ADDR) {
 				if (!nm_utils_parse_inaddr_bin (addr_family,
 				                                s,
 				                                NULL,
-				                                &info->v.addr.addr)) {
-					if (   info == &infos[PARSE_LINE_ATTR_ROUTE_VIA]
+				                                &p_data->v.addr.addr)) {
+					if (   p_info == &parse_infos[PARSE_LINE_ATTR_ROUTE_VIA]
 					    && nm_streq (s, "(null)")) {
 						/* Due to a bug, would older versions of NM write "via (null)"
 						 * (rh#1452648). Workaround that, and accept it.*/
-						memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr));
+						memset (&p_data->v.addr.addr, 0, sizeof (p_data->v.addr.addr));
 					} else {
 						if (unqualified_addr) {
 							g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -1064,15 +1108,15 @@ parse_line_type_addr_with_prefix:
 					}
 				}
 			} else {
-				nm_assert (info->type == PARSE_LINE_TYPE_ADDR_WITH_PREFIX);
-				if (   info == &infos[PARSE_LINE_ATTR_ROUTE_TO]
+				nm_assert (p_info->type == PARSE_LINE_TYPE_ADDR_WITH_PREFIX);
+				if (   p_info == &parse_infos[PARSE_LINE_ATTR_ROUTE_TO]
 				    && nm_streq (s, "default")) {
-					memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr));
+					memset (&p_data->v.addr.addr, 0, sizeof (p_data->v.addr.addr));
 					prefix = 0;
 				} else if (!nm_utils_parse_inaddr_prefix_bin (addr_family,
 				                                              s,
 				                                              NULL,
-				                                              &info->v.addr.addr,
+				                                              &p_data->v.addr.addr,
 				                                              &prefix)) {
 					g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 					             "Argument for \"%s\" is not ADDR/PREFIX format", w);
@@ -1080,10 +1124,10 @@ parse_line_type_addr_with_prefix:
 				}
 			}
 			if (prefix == -1)
-				info->v.addr.has_plen = FALSE;
+				p_data->v.addr.has_plen = FALSE;
 			else {
-				info->v.addr.has_plen = TRUE;
-				info->v.addr.plen = prefix;
+				p_data->v.addr.has_plen = TRUE;
+				p_data->v.addr.plen = prefix;
 			}
 		}
 		i_words++;
@@ -1101,79 +1145,76 @@ next:
 		route = options_route;
 		nm_ip_route_ref (route);
 	} else {
-		ParseLineInfo *info_to = &infos[PARSE_LINE_ATTR_ROUTE_TO];
-		ParseLineInfo *info_via = &infos[PARSE_LINE_ATTR_ROUTE_VIA];
-		ParseLineInfo *info_metric = &infos[PARSE_LINE_ATTR_ROUTE_METRIC];
+		ParseLineData *data_to     = &parse_datas[PARSE_LINE_ATTR_ROUTE_TO];
+		ParseLineData *data_via    = &parse_datas[PARSE_LINE_ATTR_ROUTE_VIA];
+		ParseLineData *data_metric = &parse_datas[PARSE_LINE_ATTR_ROUTE_METRIC];
 		guint prefix;
 
-		if (!info_to->has) {
+		if (!data_to->has) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Missing destination prefix");
 			return -EINVAL;
 		}
 
-		prefix =   info_to->v.addr.has_plen
-		         ? info_to->v.addr.plen
+		prefix =   data_to->v.addr.has_plen
+		         ? data_to->v.addr.plen
 		         : (addr_family == AF_INET ? 32 : 128);
 
-		if (   (   (addr_family == AF_INET  && !info_to->v.addr.addr.addr4)
-		        || (addr_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED (&info_to->v.addr.addr.addr6)))
-		    && prefix == 0) {
-			/* we ignore default routes by returning -ERANGE. */
-			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Ignore manual default route");
-			return -ERANGE;
-		}
-
 		route = nm_ip_route_new_binary (addr_family,
-		                                &info_to->v.addr.addr,
+		                                &data_to->v.addr.addr,
 		                                prefix,
-		                                info_via->has ? &info_via->v.addr.addr : NULL,
-		                                info_metric->has ? (gint64) info_metric->v.uint32 : (gint64) -1,
+		                                data_via->has ? &data_via->v.addr.addr : NULL,
+		                                data_metric->has ? (gint64) data_metric->v.uint32 : (gint64) -1,
 		                                error);
-		info_to->has = FALSE;
-		info_via->has = FALSE;
-		info_metric->has = FALSE;
+		data_to->has = FALSE;
+		data_via->has = FALSE;
+		data_metric->has = FALSE;
 		if (!route)
 			return -EINVAL;
 	}
 
-	for (i = 0; i < G_N_ELEMENTS (infos); i++) {
-		ParseLineInfo *info = &infos[i];
+	for (i = 0; i < G_N_ELEMENTS (parse_infos); i++) {
+		const ParseLineInfo *p_info = &parse_infos[i];
+		ParseLineData *p_data = &parse_datas[i];
 
-		if (!info->has)
+		if (!p_data->has)
 			continue;
-		if (info->ignore || info->disabled)
+
+		if (   (p_info->ignore & af_flag)
+		    || (p_info->disabled & af_flag)
+		    || (p_info->disabled_with_options_route && options_route))
 			continue;
-		switch (info->type) {
+
+		switch (p_info->type) {
 		case PARSE_LINE_TYPE_UINT8:
+		case PARSE_LINE_TYPE_ROUTE_SCOPE:
 			nm_ip_route_set_attribute (route,
-			                           info->key,
-			                           g_variant_new_byte (info->v.uint8));
+			                           p_info->key,
+			                           g_variant_new_byte (p_data->v.uint8));
 			break;
 		case PARSE_LINE_TYPE_UINT32:
 			nm_ip_route_set_attribute (route,
-			                           info->key,
-			                           g_variant_new_uint32 (info->v.uint32));
+			                           p_info->key,
+			                           g_variant_new_uint32 (p_data->v.uint32));
 			break;
 		case PARSE_LINE_TYPE_UINT32_WITH_LOCK:
-			if (info->v.uint32_with_lock.lock) {
+			if (p_data->v.uint32_with_lock.lock) {
 				nm_ip_route_set_attribute (route,
-				                           nm_sprintf_buf (buf1, "lock-%s", info->key),
+				                           nm_sprintf_buf (buf1, "lock-%s", p_info->key),
 				                           g_variant_new_boolean (TRUE));
 			}
 			nm_ip_route_set_attribute (route,
-			                           info->key,
-			                           g_variant_new_uint32 (info->v.uint32_with_lock.uint32));
+			                           p_info->key,
+			                           g_variant_new_uint32 (p_data->v.uint32_with_lock.uint32));
 			break;
 		case PARSE_LINE_TYPE_ADDR:
 		case PARSE_LINE_TYPE_ADDR_WITH_PREFIX:
 			nm_ip_route_set_attribute (route,
-			                           info->key,
+			                           p_info->key,
 			                           g_variant_new_printf ("%s%s",
-			                                                 inet_ntop (addr_family, &info->v.addr.addr, buf1, sizeof (buf1)),
-			                                                 info->v.addr.has_plen
-			                                                    ? nm_sprintf_buf (buf2, "/%u", (unsigned) info->v.addr.plen)
+			                                                 inet_ntop (addr_family, &p_data->v.addr.addr, buf1, sizeof (buf1)),
+			                                                 p_data->v.addr.has_plen
+			                                                    ? nm_sprintf_buf (buf2, "/%u", (unsigned) p_data->v.addr.plen)
 			                                                    : ""));
 			break;
 		case PARSE_LINE_TYPE_FLAG:
@@ -1182,7 +1223,7 @@ next:
 			 * of this attribute, hence, the file format cannot encode
 			 * that configuration. */
 			nm_ip_route_set_attribute (route,
-			                           info->key,
+			                           p_info->key,
 			                           g_variant_new_boolean (TRUE));
 			break;
 		default:
@@ -1244,7 +1285,7 @@ read_one_ip4_route (shvarFile *ifcfg,
 		return FALSE;
 	if (has_key) {
 		prefix = nm_utils_ip4_netmask_to_prefix (netmask);
-		if (prefix == 0 || netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) {
+		if (netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf));
 			return FALSE;
@@ -1538,7 +1579,7 @@ make_ip4_setting (shvarFile *ifcfg,
 	gboolean has_key;
 	shvarFile *route_ifcfg;
 	gboolean never_default;
-	gint64 timeout;
+	gint64 i64;
 	int priority;
 	const char *const *item;
 	guint32 route_table;
@@ -1641,6 +1682,14 @@ make_ip4_setting (shvarFile *ifcfg,
 		              NULL);
 	}
 
+	i64 = svGetValueInt64 (ifcfg, "DHCP_HOSTNAME_FLAGS", 10, 0, G_MAXUINT32, -1);
+	if (i64 > -1) {
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS,
+		              (guint) i64,
+		              NULL);
+	}
+
 	g_object_set (s_ip4,
 	              NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE),
 	              NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
@@ -1651,6 +1700,11 @@ make_ip4_setting (shvarFile *ifcfg,
 	if (v)
 		g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, v, NULL);
 
+	nm_clear_g_free (&value);
+	v = svGetValueStr (ifcfg, "DHCP_IAID", &value);
+	if (v)
+		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_IAID, v, NULL);
+
 	/* Read static IP addresses.
 	 * Read them even for AUTO method - in this case the addresses are
 	 * added to the automatic ones. Note that this is not currently supported by
@@ -1806,14 +1860,14 @@ make_ip4_setting (shvarFile *ifcfg,
 		}
 	}
 
-	timeout = svGetValueInt64 (ifcfg, "ACD_TIMEOUT", 10, -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -2);
-	if (timeout == -2) {
-		timeout = svGetValueInt64 (ifcfg, "ARPING_WAIT", 10, -1,
-		                           NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, -1);
-		if (timeout > 0)
-			timeout *= 1000;
+	i64 = svGetValueInt64 (ifcfg, "ACD_TIMEOUT", 10, -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -2);
+	if (i64 == -2) {
+		i64 = svGetValueInt64 (ifcfg, "ARPING_WAIT", 10, -1,
+		                       NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, -1);
+		if (i64 > 0)
+			i64 *= 1000;
 	}
-	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (int) timeout, NULL);
+	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (int) i64, NULL);
 
 	return NM_SETTING (g_steal_pointer (&s_ip4));
 }
@@ -1947,6 +2001,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	gs_free const char **list = NULL;
 	const char *const *iter;
 	guint32 i;
+	gint64 i64;
 	int i_val;
 	GError *local = NULL;
 	int priority;
@@ -2085,6 +2140,11 @@ make_ip6_setting (shvarFile *ifcfg,
 		g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_DHCP_DUID, v, NULL);
 
 	nm_clear_g_free (&value);
+	v = svGetValueStr (ifcfg, "DHCPV6_IAID", &value);
+	if (v)
+		g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_IAID, v, NULL);
+
+	nm_clear_g_free (&value);
 	v = svGetValueStr (ifcfg, "DHCPV6_HOSTNAME", &value);
 	/* Use DHCP_HOSTNAME as fallback if it is in FQDN format and ipv6.method is
 	 * auto or dhcp: this is required to support old ifcfg files
@@ -2102,6 +2162,15 @@ make_ip6_setting (shvarFile *ifcfg,
 	g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME,
 	              svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL);
 
+
+	i64 = svGetValueInt64 (ifcfg, "DHCPV6_HOSTNAME_FLAGS", 10, 0, G_MAXUINT32, -1);
+	if (i64 > -1) {
+		g_object_set (s_ip6,
+		              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS,
+		              (guint) i64,
+		              NULL);
+	}
+
 	/* Read static IP addresses.
 	 * Read them even for AUTO and DHCP methods - in this case the addresses are
 	 * added to the automatic ones. Note that this is not currently supported by
@@ -2762,7 +2831,7 @@ add_one_wep_key (shvarFile *ifcfg,
 			/* Hexadecimal WEP key */
 			if (NM_STRCHAR_ANY (value, ch, !g_ascii_isxdigit (ch))) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-				             "Invalid hexadecimal WEP key.");
+				             "Invalid hexadecimal WEP key");
 				return FALSE;
 			}
 			key = value;
@@ -2771,7 +2840,7 @@ add_one_wep_key (shvarFile *ifcfg,
 			/* ASCII key */
 			if (NM_STRCHAR_ANY (value + 2, ch, !g_ascii_isprint (ch))) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-				             "Invalid ASCII WEP key.");
+				             "Invalid ASCII WEP key");
 				return FALSE;
 			}
 
@@ -2787,7 +2856,7 @@ add_one_wep_key (shvarFile *ifcfg,
 
 	if (!key) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Invalid WEP key length.");
+		             "Invalid WEP key length");
 		return FALSE;
 	}
 
@@ -2934,7 +3003,7 @@ make_wep_setting (shvarFile *ifcfg,
 		if (auth_alg && !strcmp (auth_alg, "shared")) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "WEP Shared Key authentication is invalid for "
-			             "unencrypted connections.");
+			             "unencrypted connections");
 			return NULL;
 		}
 
@@ -2963,22 +3032,6 @@ fill_wpa_ciphers (shvarFile *ifcfg,
 
 	list = nm_utils_strsplit_set (p, " ");
 	for (iter = list; iter && *iter; iter++, i++) {
-		/* Ad-Hoc configurations cannot have pairwise ciphers, and can only
-		 * have one group cipher.  Ignore any additional group ciphers and
-		 * any pairwise ciphers specified.
-		 */
-		if (adhoc) {
-			if (group && (i > 0)) {
-				PARSE_WARNING ("ignoring group cipher '%s' (only one group cipher allowed "
-				               "in Ad-Hoc mode)", *iter);
-				continue;
-			} else if (!group) {
-				PARSE_WARNING ("ignoring pairwise cipher '%s' (pairwise not used "
-				               "in Ad-Hoc mode)", *iter);
-				continue;
-			}
-		}
-
 		if (!strcmp (*iter, "CCMP")) {
 			if (group)
 				nm_setting_wireless_security_add_group (wsec, "ccmp");
@@ -3184,6 +3237,89 @@ eap_tls_reader (const char *eap_method,
 }
 
 static gboolean
+parse_8021x_phase2_auth (shvarFile *ifcfg,
+                         shvarFile *keys_ifcfg,
+                         NMSetting8021x *s_8021x,
+                         GError **error)
+{
+	gs_free char *inner_auth = NULL;
+	gs_free char *v_free = NULL;
+	const char *v;
+	gs_free const char **list = NULL;
+	const char *const *iter;
+	guint num_auth = 0;
+	guint num_autheap = 0;
+
+	v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &v_free);
+	if (!v) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "Missing IEEE_8021X_INNER_AUTH_METHODS");
+		return FALSE;
+	}
+
+	inner_auth = g_ascii_strdown (v, -1);
+	list = nm_utils_strsplit_set (inner_auth, " ");
+	for (iter = list; iter && *iter; iter++) {
+		if (NM_IN_STRSET (*iter, "pap",
+		                         "chap",
+		                         "mschap",
+		                         "mschapv2",
+		                         "gtc",
+		                         "otp",
+		                         "md5")) {
+			if (num_auth == 0) {
+				if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
+					return FALSE;
+				g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL);
+			}
+			num_auth++;
+		} else if (nm_streq (*iter, "tls")) {
+			if (num_auth == 0) {
+				if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
+					return FALSE;
+				g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, "tls", NULL);
+			}
+			num_auth++;
+		} else if (NM_IN_STRSET (*iter, "eap-md5",
+		                                "eap-mschapv2",
+		                                "eap-otp",
+		                                "eap-gtc")) {
+			if (num_autheap == 0) {
+				if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
+					return FALSE;
+				g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL);
+			}
+			num_autheap++;
+		} else if (nm_streq (*iter, "eap-tls")) {
+			if (num_autheap == 0) {
+				if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
+					return FALSE;
+				g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL);
+			}
+			num_autheap++;
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'",
+			             *iter);
+			return FALSE;
+		}
+	}
+
+	if (num_auth > 1)
+		PARSE_WARNING ("Discarded extra phase2 authentication methods");
+	if (num_auth > 1)
+		PARSE_WARNING ("Discarded extra phase2 EAP authentication methods");
+
+	if (!num_auth && !num_autheap) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "No phase2 authentication method found");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
 eap_peap_reader (const char *eap_method,
                  shvarFile *ifcfg,
                  shvarFile *keys_ifcfg,
@@ -3193,8 +3329,6 @@ eap_peap_reader (const char *eap_method,
 {
 	gs_free char *value = NULL;
 	const char *v;
-	gs_free const char **list = NULL;
-	const char *const *iter;
 
 	if (!_cert_set_from_ifcfg (s_8021x,
 	                           ifcfg,
@@ -3232,46 +3366,8 @@ eap_peap_reader (const char *eap_method,
 	if (v)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL);
 
-	nm_clear_g_free (&value);
-	v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value);
-	if (!v) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
-		return FALSE;
-	}
-
-	/* Handle options for the inner auth method */
-	list = nm_utils_strsplit_set (v, " ");
-	iter = list;
-	if (iter) {
-		if (NM_IN_STRSET (*iter, "MSCHAPV2",
-		                         "MD5",
-		                         "GTC")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
-				return FALSE;
-		} else if (nm_streq (*iter, "TLS")) {
-			if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
-				return FALSE;
-		} else {
-			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
-			             *iter);
-			return FALSE;
-		}
-
-		{
-			gs_free char *lower = NULL;
-
-			lower = g_ascii_strdown (*iter, -1);
-			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
-		}
-	}
-
-	if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "No valid IEEE_8021X_INNER_AUTH_METHODS found.");
+	if (!parse_8021x_phase2_auth (ifcfg, keys_ifcfg, s_8021x, error))
 		return FALSE;
-	}
 
 	return TRUE;
 }
@@ -3284,11 +3380,8 @@ eap_ttls_reader (const char *eap_method,
                  gboolean phase2,
                  GError **error)
 {
-	gs_free char *inner_auth = NULL;
 	gs_free char *value = NULL;
 	const char *v;
-	gs_free const char **list = NULL;
-	const char *const *iter;
 
 	if (!_cert_set_from_ifcfg (s_8021x,
 	                           ifcfg,
@@ -3308,44 +3401,8 @@ eap_ttls_reader (const char *eap_method,
 	if (v)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL);
 
-	nm_clear_g_free (&value);
-	v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value);
-	if (!v) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
+	if (!parse_8021x_phase2_auth (ifcfg, keys_ifcfg, s_8021x, error))
 		return FALSE;
-	}
-
-	inner_auth = g_ascii_strdown (v, -1);
-
-	/* Handle options for the inner auth method */
-	list = nm_utils_strsplit_set (inner_auth, " ");
-	iter = list;
-	if (iter) {
-		if (NM_IN_STRSET (*iter, "mschapv2",
-		                         "mschap",
-		                         "pap",
-		                         "chap")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
-				return FALSE;
-			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL);
-		} else if (nm_streq (*iter, "eap-tls")) {
-			if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
-				return FALSE;
-			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL);
-		} else if (NM_IN_STRSET (*iter, "eap-mschapv2",
-		                                "eap-md5",
-		                                "eap-gtc")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
-				return FALSE;
-			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL);
-		} else {
-			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
-			             *iter);
-			return FALSE;
-		}
-	}
 
 	return TRUE;
 }
@@ -3358,17 +3415,13 @@ eap_fast_reader (const char *eap_method,
                  gboolean phase2,
                  GError **error)
 {
-	char *anon_ident = NULL;
-	char *pac_file = NULL;
-	char *real_pac_path = NULL;
-	char *inner_auth = NULL;
-	char *fast_provisioning = NULL;
-	char *lower;
-	gs_free const char **list = NULL;
+	gs_free char *anon_ident = NULL;
+	gs_free char *pac_file = NULL;
+	gs_free char *real_pac_path = NULL;
+	gs_free char *fast_provisioning = NULL;
 	const char *const *iter;
 	const char *pac_prov_str;
 	gboolean allow_unauth = FALSE, allow_auth = FALSE;
-	gboolean success = FALSE;
 
 	pac_file = svGetValueStr_cp (ifcfg, "IEEE_8021X_PAC_FILE");
 	if (pac_file) {
@@ -3378,10 +3431,10 @@ eap_fast_reader (const char *eap_method,
 
 	fast_provisioning = svGetValueStr_cp (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
 	if (fast_provisioning) {
-		gs_free const char **list1 = NULL;
+		gs_free const char **list = NULL;
 
-		list1 = nm_utils_strsplit_set (fast_provisioning, " \t");
-		for (iter = list1; iter && *iter; iter++) {
+		list = nm_utils_strsplit_set (fast_provisioning, " \t");
+		for (iter = list; iter && *iter; iter++) {
 			if (strcmp (*iter, "allow-unauth") == 0)
 				allow_unauth = TRUE;
 			else if (strcmp (*iter, "allow-auth") == 0)
@@ -3398,56 +3451,18 @@ eap_fast_reader (const char *eap_method,
 
 	if (!pac_file && !(allow_unauth || allow_auth)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "IEEE_8021X_PAC_FILE not provided and EAP-FAST automatic PAC provisioning disabled.");
-		goto done;
+		             "IEEE_8021X_PAC_FILE not provided and EAP-FAST automatic PAC provisioning disabled");
+		return FALSE;
 	}
 
 	anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
 	if (anon_ident)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
 
-	inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
-	if (!inner_auth) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
-		goto done;
-	}
-
-	/* Handle options for the inner auth method */
-	list = nm_utils_strsplit_set (inner_auth, " ");
-	iter = list;
-	if (iter) {
-		if (   !strcmp (*iter, "MSCHAPV2")
-		    || !strcmp (*iter, "GTC")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
-				goto done;
-		} else {
-			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
-			             *iter);
-			goto done;
-		}
-
-		lower = g_ascii_strdown (*iter, -1);
-		g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
-		g_free (lower);
-	}
-
-	if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "No valid IEEE_8021X_INNER_AUTH_METHODS found.");
-		goto done;
-	}
-
-	success = TRUE;
+	if (!parse_8021x_phase2_auth (ifcfg, keys_ifcfg, s_8021x, error))
+		return FALSE;
 
-done:
-	g_free (inner_auth);
-	g_free (fast_provisioning);
-	g_free (real_pac_path);
-	g_free (pac_file);
-	g_free (anon_ident);
-	return success;
+	return TRUE;
 }
 
 typedef struct {
@@ -3546,7 +3561,7 @@ fill_8021x (shvarFile *ifcfg,
 			 * used with TTLS or PEAP or whatever.
 			 */
 			if (wifi && eap->wifi_phase2_only) {
-				PARSE_WARNING ("ignored invalid IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi.",
+				PARSE_WARNING ("ignored invalid IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi",
 				               lower);
 				goto next;
 			}
@@ -3564,12 +3579,12 @@ next:
 		}
 
 		if (!found)
-			PARSE_WARNING ("ignored unknown IEEE_8021X_EAP_METHOD '%s'.", lower);
+			PARSE_WARNING ("ignored unknown IEEE_8021X_EAP_METHOD '%s'", lower);
 	}
 
 	if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "No valid EAP methods found in IEEE_8021X_EAP_METHODS.");
+		             "No valid EAP methods found in IEEE_8021X_EAP_METHODS");
 		return NULL;
 	}
 
@@ -3663,8 +3678,8 @@ make_wpa_setting (shvarFile *ifcfg,
 
 	/* WPA and/or RSN */
 	if (adhoc) {
-		/* Ad-Hoc mode only supports WPA proto for now */
-		nm_setting_wireless_security_add_proto (wsec, "wpa");
+		/* Ad-Hoc mode only supports RSN proto */
+		nm_setting_wireless_security_add_proto (wsec, "rsn");
 	} else {
 		gs_free char *value2 = NULL;
 		const char *v2;
@@ -3698,9 +3713,7 @@ make_wpa_setting (shvarFile *ifcfg,
 			}
 		}
 
-		if (adhoc)
-			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL);
-		else if (wpa_psk)
+		if (wpa_psk)
 			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NULL);
 		else {
 			nm_assert (wpa_sae);
@@ -4145,7 +4158,7 @@ wireless_connection_from_ifcfg (const char *file,
 
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create connection setting.");
+		             "Failed to create connection setting");
 		g_object_unref (connection);
 		return NULL;
 	}
@@ -4664,7 +4677,7 @@ make_wired_setting (shvarFile *ifcfg,
 		g_set_error (error,
 		             NM_UTILS_ERROR,
 		             NM_UTILS_ERROR_SETTING_MISSING,
-		             "The setting is missing.");
+		             "The setting is missing");
 		return NULL;
 	}
 
@@ -4690,7 +4703,7 @@ wired_connection_from_ifcfg (const char *file,
 	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_WIRED_SETTING_NAME, NULL, NULL);
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create connection setting.");
+		             "Failed to create connection setting");
 		g_object_unref (connection);
 		return NULL;
 	}
@@ -4768,7 +4781,7 @@ parse_infiniband_p_key (shvarFile *ifcfg,
 
 	if (!ret) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create InfiniBand setting.");
+		             "Failed to create InfiniBand setting");
 	}
 	return ret;
 }
@@ -4842,7 +4855,7 @@ infiniband_connection_from_ifcfg (const char *file,
 	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL);
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create connection setting.");
+		             "Failed to create connection setting");
 		g_object_unref (connection);
 		return NULL;
 	}
@@ -4946,7 +4959,7 @@ bond_connection_from_ifcfg (const char *file,
 	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_BOND_SETTING_NAME, NULL, _("Bond"));
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create connection setting.");
+		             "Failed to create connection setting");
 		g_object_unref (connection);
 		return NULL;
 	}
@@ -5019,7 +5032,7 @@ team_connection_from_ifcfg (const char *file,
 	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_TEAM_SETTING_NAME, NULL, _("Team"));
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create connection setting.");
+		             "Failed to create connection setting");
 		g_object_unref (connection);
 		return NULL;
 	}
@@ -5301,7 +5314,7 @@ bridge_connection_from_ifcfg (const char *file,
 	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_BRIDGE_SETTING_NAME, NULL, _("Bridge"));
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create connection setting.");
+		             "Failed to create connection setting");
 		g_object_unref (connection);
 		return NULL;
 	}
@@ -5469,7 +5482,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	iface_name = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (!iface_name && vlan_id < 0) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		                     "Missing DEVICE property; cannot determine VLAN ID.");
+		                     "Missing DEVICE property; cannot determine VLAN ID");
 		return NULL;
 	}
 
@@ -5512,7 +5525,7 @@ make_vlan_setting (shvarFile *ifcfg,
 
 	if (vlan_id < 0) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		                     "Failed to determine VLAN ID from DEVICE or VLAN_ID.");
+		                     "Failed to determine VLAN ID from DEVICE or VLAN_ID");
 		return NULL;
 	}
 	g_object_set (s_vlan, NM_SETTING_VLAN_ID, vlan_id, NULL);
@@ -5583,7 +5596,7 @@ vlan_connection_from_ifcfg (const char *file,
 	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_VLAN_SETTING_NAME, NULL, "Vlan");
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Failed to create connection setting.");
+		             "Failed to create connection setting");
 		g_object_unref (connection);
 		return NULL;
 	}
@@ -5726,7 +5739,7 @@ connection_from_file_full (const char *filename,
 	ifcfg_name = utils_get_ifcfg_name (filename, TRUE);
 	if (!ifcfg_name) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		             "Ignoring connection '%s' because it's not an ifcfg file.", filename);
+		             "Ignoring connection '%s' because it's not an ifcfg file", filename);
 		return NULL;
 	}
 
@@ -5804,14 +5817,14 @@ connection_from_file_full (const char *filename,
 		device = svGetValueStr_cp (main_ifcfg, "DEVICE");
 		if (!device) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "File '%s' had neither TYPE nor DEVICE keys.", filename);
+			             "File '%s' had neither TYPE nor DEVICE keys", filename);
 			return NULL;
 		}
 
 		if (!strcmp (device, "lo")) {
 			NM_SET_OUT (out_ignore_error, TRUE);
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Ignoring loopback device config.");
+			             "Ignoring loopback device config");
 			g_free (device);
 			return NULL;
 		}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
index 8008e052..9319a064 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
index 2841bedb..79101296 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h
index e1165f50..1b2b58e6 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2019 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index cb1fc23a..ee389bd9 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -1,20 +1,6 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 - 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
index 20d6f72d..035146ff 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -1,20 +1,6 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 - 2017 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2008 - 2017 Red Hat, Inc.
  */
 
 #ifndef _UTILS_H_
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 809d3769..d33845c2 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -1,20 +1,6 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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 2009 - 2015 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2009 - 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -305,6 +291,7 @@ write_blobs (GHashTable *blobs, GError **error)
 		                                 (const char *) g_bytes_get_data (blob, NULL),
 		                                 g_bytes_get_size (blob),
 		                                 0600,
+		                                 NULL,
 		                                 &write_error)) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "Could not write certificate to file \"%s\": %s",
@@ -588,7 +575,7 @@ write_wireless_security_setting (NMConnection *connection,
 		svUnsetValue (ifcfg, "KEY_MGMT");
 		wep = TRUE;
 		*no_8021x = TRUE;
-	} else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
+	} else if (!strcmp (key_mgmt, "wpa-psk")) {
 		svSetValueStr (ifcfg, "KEY_MGMT", "WPA-PSK");
 		wpa = TRUE;
 		*no_8021x = TRUE;
@@ -1943,7 +1930,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		 * it into an interface name, so that legacy tooling is not confused. */
 		if (!nm_utils_get_testing ()) {
 			/* This is conditional for easier testing. */
-			master_iface = nm_manager_iface_for_uuid (nm_manager_get (), master);
+			master_iface = nm_manager_iface_for_uuid (NM_MANAGER_GET, master);
 		}
 		if (!master_iface) {
 			master_iface = master;
@@ -2097,6 +2084,8 @@ get_route_attributes_string (NMIPRoute *route, int family)
 				/* we also have a corresponding attribute with the numeric value. The
 				 * lock setting is handled above. */
 			}
+		} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SCOPE)) {
+			g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_byte (attr));
 		} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) {
 			g_string_append_printf (str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte (attr));
 		} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TABLE)) {
@@ -2449,6 +2438,7 @@ write_ip4_setting (NMConnection *connection,
 	GString *searches;
 	const char *method = NULL;
 	gboolean has_netmask;
+	NMDhcpHostnameFlags flags;
 
 	NM_SET_OUT (out_route_content_svformat, NULL);
 	NM_SET_OUT (out_route_content, NULL);
@@ -2609,6 +2599,12 @@ write_ip4_setting (NMConnection *connection,
 	value = nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4));
 	svSetValueStr (ifcfg, "DHCP_FQDN", value);
 
+	flags = nm_setting_ip_config_get_dhcp_hostname_flags (s_ip4);
+	svSetValueInt64_cond (ifcfg,
+	                      "DHCP_HOSTNAME_FLAGS",
+	                      flags != NM_DHCP_HOSTNAME_FLAG_NONE,
+	                      flags);
+
 	/* Missing DHCP_SEND_HOSTNAME means TRUE, and we prefer not write it explicitly
 	 * in that case, because it is NM-specific variable
 	 */
@@ -2618,6 +2614,9 @@ write_ip4_setting (NMConnection *connection,
 	value = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4));
 	svSetValueStr (ifcfg, "DHCP_CLIENT_ID", value);
 
+	value = nm_setting_ip_config_get_dhcp_iaid (s_ip4);
+	svSetValueStr (ifcfg, "DHCP_IAID", value);
+
 	timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip4);
 	svSetValueInt64_cond (ifcfg,
 	                      "IPV4_DHCP_TIMEOUT",
@@ -2752,6 +2751,7 @@ write_ip4_aliases (NMConnection *connection, const char *base_ifcfg_path)
 static void
 write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg)
 {
+	NMDhcpHostnameFlags flags;
 	const char *hostname;
 
 	hostname = nm_setting_ip_config_get_dhcp_hostname (s_ip6);
@@ -2764,6 +2764,12 @@ write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg)
 		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
 	else
 		svSetValueStr (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
+
+	flags = nm_setting_ip_config_get_dhcp_hostname_flags (s_ip6);
+	svSetValueInt64_cond (ifcfg,
+	                      "DHCPV6_HOSTNAME_FLAGS",
+	                      flags != NM_DHCP_HOSTNAME_FLAG_NONE,
+	                      flags);
 }
 
 static gboolean
@@ -2796,6 +2802,7 @@ write_ip6_setting (NMConnection *connection,
 		svUnsetValue (ifcfg, "IPV6_AUTOCONF");
 		svUnsetValue (ifcfg, "DHCPV6C");
 		svUnsetValue (ifcfg, "DHCPv6_DUID");
+		svUnsetValue (ifcfg, "DHCPv6_IAID");
 		svUnsetValue (ifcfg, "DHCPV6_HOSTNAME");
 		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
 		svUnsetValue (ifcfg, "IPV6_DEFROUTE");
@@ -2845,6 +2852,8 @@ write_ip6_setting (NMConnection *connection,
 
 	svSetValueStr (ifcfg, "DHCPV6_DUID",
 	               nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6)));
+	svSetValueStr (ifcfg, "DHCPV6_IAID",
+	               nm_setting_ip_config_get_dhcp_iaid (s_ip6));
 
 	write_ip6_setting_dhcp_hostname (s_ip6, ifcfg);
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
index 0902daee..c4903a46 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2009 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index d4bc71f3..16b2dd37 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -1,25 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * shvar.c
- *
- * Implementation of non-destructively reading/writing files containing
- * only shell variable declarations and full-line comments.
- *
- * Copyright 1999,2000 Red Hat, Inc.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+ * Copyright (C) 1999, 2000 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -790,7 +771,7 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 	shvarFile *s;
 	gboolean closefd = FALSE;
 	int errsv = 0;
-	char *arena;
+	gs_free char *arena = NULL;
 	const char *p, *q;
 	gs_free_error GError *local = NULL;
 	nm_auto_close int fd = -1;
@@ -816,13 +797,14 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 		return NULL;
 	}
 
-	if (nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd,
-	                              closefd,
-	                              10 * 1024 * 1024,
-	                              NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                              &arena,
-	                              NULL,
-	                              &local) < 0) {
+	if (!nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd,
+	                               closefd,
+	                               10 * 1024 * 1024,
+	                               NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+	                               &arena,
+	                               NULL,
+	                               NULL,
+	                               &local)) {
 		if (create)
 			return svFile_new (name);
 
@@ -839,7 +821,6 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 		c_list_link_tail (&s->lst_head, &line_new_parse (p, q - p)->lst);
 	if (p[0])
 		c_list_link_tail (&s->lst_head, &line_new_parse (p, strlen (p))->lst);
-	g_free (arena);
 
 	/* closefd is set if we opened the file read-only, so go ahead and
 	 * close it, because we can't write to it anyway */
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index 2f6912b3..c3bbabab 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -1,32 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * shvar.h
- *
- * Interface for non-destructively reading/writing files containing
- * only shell variable declarations and full-line comments.
- *
- * Includes explicit inheritance mechanism intended for use with
- * Red Hat Linux ifcfg-* files.  There is no protection against
- * inheritance loops; they will generally cause stack overflows.
- * Furthermore, they are only intended for one level of inheritance;
- * the value setting algorithm assumes this.
- *
- * Copyright 1999 Red Hat, Inc.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+ * Copyright (C) 1999 Red Hat, Inc.
  */
+
 #ifndef _SHVAR_H
 #define _SHVAR_H
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/meson.build b/src/settings/plugins/ifcfg-rh/tests/meson.build
index f65494bb..21699045 100644
--- a/src/settings/plugins/ifcfg-rh/tests/meson.build
+++ b/src/settings/plugins/ifcfg-rh/tests/meson.build
@@ -1,11 +1,10 @@
 test_unit = 'test-ifcfg-rh'
 
-test_ifcfg_dir = meson.current_source_dir()
-
 exe = executable(
   test_unit,
   test_unit + '.c',
-  dependencies: test_nm_dep,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
   link_with: libnms_ifcfg_rh_core,
 )
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc
index aa00925e..c3cadbb8 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc
@@ -12,5 +12,6 @@ USERCTL=yes
 PEERDNS=yes
 IPV6INIT=no
 CIPHER_GROUP=CCMP
+CIPHER_PAIRWISE=CCMP
 KEY_MGMT=WPA-PSK
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp
index 727d2ceb..5d36675a 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp
@@ -8,7 +8,12 @@ USERCTL=yes
 IPV6INIT=no
 NM_CONTROLLED=yes
 PEERDNS=no
-DHCP_HOSTNAME=foobar
+DHCP_FQDN=foo.bar
+DHCP_HOSTNAME_FLAGS=6
 DNS1=4.2.2.1
 DNS2=4.2.2.2
-
+IPV6_AUTOCONF=no
+IPV6INIT=yes
+DHCPV6C=yes
+DHCPV6_HOSTNAME_FLAGS=8
+DHCPV6_HOSTNAME=foo.bar
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
index 8d6aaac2..5d02c62e 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
@@ -6,10 +6,10 @@ ADDRESS1=44.55.66.77
 NETMASK1=255.255.255.255
 GATEWAY1=192.168.1.7
 METRIC1=3
-OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 initcwnd lock 13 initrwnd 14"
+OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 scope 10 initcwnd lock 13 initrwnd 14"
 
 ADDRESS2=44.55.66.78
 NETMASK2=255.255.255.255
 GATEWAY2=192.168.1.8
 METRIC2=3
-OPTIONS2="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 onlink window 30000 initcwnd lock 13 initrwnd 14"
+OPTIONS2="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 onlink window 30000 initcwnd lock 13 initrwnd 14 scope link"
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 45e90b91..675421d3 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
 
@@ -901,6 +887,7 @@ test_read_wired_dhcp (void)
 	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
 	char *unmanaged = NULL;
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0xee };
 	const char *mac;
@@ -930,11 +917,23 @@ test_read_wired_dhcp (void)
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	g_assert (s_ip4);
 	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
-	g_assert_cmpstr (nm_setting_ip_config_get_dhcp_hostname (s_ip4), ==, "foobar");
+	g_assert_cmpstr (nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4)), ==, "foo.bar");
 	g_assert (nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
 	g_assert_cmpuint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 2);
 	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "4.2.2.1");
 	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "4.2.2.2");
+	g_assert_cmpuint (nm_setting_ip_config_get_dhcp_hostname_flags (s_ip4),
+	                  ==,
+	                  NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED | NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE);
+
+	/* ===== IPv6 SETTING ===== */
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (s_ip6);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DHCP);
+	g_assert_cmpstr (nm_setting_ip_config_get_dhcp_hostname (s_ip6), ==, "foo.bar");
+	g_assert_cmpuint (nm_setting_ip_config_get_dhcp_hostname_flags (s_ip6),
+	                  ==,
+	                  NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS);
 
 	g_object_unref (connection);
 }
@@ -1348,6 +1347,7 @@ test_read_wired_static_routes (void)
 	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
 	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE);
 	nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1");
+	nmtst_assert_route_attribute_byte (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SCOPE, 10);
 
 	ip4_route = nm_setting_ip_config_get_route (s_ip4, 2);
 	g_assert (ip4_route);
@@ -1365,6 +1365,7 @@ test_read_wired_static_routes (void)
 	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE);
 	nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1");
 	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, TRUE);
+	nmtst_assert_route_attribute_byte (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SCOPE, 253);
 
 	g_object_unref (connection);
 }
@@ -1520,10 +1521,8 @@ test_read_wired_ipv6_manual (void)
 	NMIPAddress *ip6_addr;
 	NMIPRoute *ip6_route;
 
-	NMTST_EXPECT_NM_WARN ("*ignoring manual default route*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-ipv6-manual",
 	                                    NULL, TYPE_ETHERNET, &unmanaged);
-	g_test_assert_expected_messages ();
 	g_assert (!unmanaged);
 
 	/* ===== CONNECTION SETTING ===== */
@@ -1581,7 +1580,7 @@ test_read_wired_ipv6_manual (void)
 	g_assert_cmpint (nm_ip_address_get_prefix (ip6_addr), ==, 96);
 
 	/* Routes */
-	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 3);
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 4);
 	/* Route #1 */
 	ip6_route = nm_setting_ip_config_get_route (s_ip6, 0);
 	g_assert (ip6_route);
@@ -1592,12 +1591,19 @@ test_read_wired_ipv6_manual (void)
 	/* Route #2 */
 	ip6_route = nm_setting_ip_config_get_route (s_ip6, 1);
 	g_assert (ip6_route);
+	g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "::");
+	g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 0);
+	g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, "dead::beaf");
+	g_assert_cmpint (nm_ip_route_get_metric (ip6_route), ==, -1);
+	/* Route #3 */
+	ip6_route = nm_setting_ip_config_get_route (s_ip6, 2);
+	g_assert (ip6_route);
 	g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "abbe::cafe");
 	g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);
 	g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, NULL);
 	g_assert_cmpint (nm_ip_route_get_metric (ip6_route), ==, 777);
-	/* Route #3 */
-	ip6_route = nm_setting_ip_config_get_route (s_ip6, 2);
+	/* Route #4 */
+	ip6_route = nm_setting_ip_config_get_route (s_ip6, 3);
 	g_assert (ip6_route);
 	g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "aaaa::cccc");
 	g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);
@@ -3128,17 +3134,17 @@ test_read_wifi_wpa_psk_adhoc (void)
 
 	s_wsec = nm_connection_get_setting_wireless_security (connection);
 	g_assert (s_wsec);
-	g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "wpa-none");
+	g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "wpa-psk");
 	g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, "I wonder what the king is doing tonight?");
 
-	/* Pairwise cipher is unused in adhoc mode */
-	g_assert_cmpint (nm_setting_wireless_security_get_num_pairwise (s_wsec), ==, 0);
+	g_assert_cmpint (nm_setting_wireless_security_get_num_pairwise (s_wsec), ==, 1);
+	g_assert_cmpstr (nm_setting_wireless_security_get_pairwise (s_wsec, 0), ==, "ccmp");
 
 	g_assert_cmpint (nm_setting_wireless_security_get_num_groups (s_wsec), ==, 1);
 	g_assert_cmpstr (nm_setting_wireless_security_get_group (s_wsec, 0), ==, "ccmp");
 
 	g_assert_cmpint (nm_setting_wireless_security_get_num_protos (s_wsec), ==, 1);
-	g_assert_cmpstr (nm_setting_wireless_security_get_proto (s_wsec, 0), ==, "wpa");
+	g_assert_cmpstr (nm_setting_wireless_security_get_proto (s_wsec, 0), ==, "rsn");
 
 	/* ===== IPv4 SETTING ===== */
 
@@ -4464,9 +4470,11 @@ test_write_wired_dhcp (void)
 	g_object_set (s_ip4,
 	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
 	              NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, "random-client-id-00:22:33",
-	              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, "awesome-hostname",
+	              NM_SETTING_IP4_CONFIG_DHCP_FQDN, "awesome.hostname",
+	              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS, (guint) NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED,
 	              NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, TRUE,
 	              NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, TRUE,
+	              NM_SETTING_IP_CONFIG_DHCP_IAID, "2864434397",
 	              NULL);
 
 	nmtst_assert_connection_verifies (connection);
@@ -4476,8 +4484,10 @@ test_write_wired_dhcp (void)
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
 	g_object_set (s_ip6,
-	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DHCP,
 	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+	              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, "awesome.hostname",
+	              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS, (guint) NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE,
 	              NULL);
 
 	_writer_new_connection (connection,
@@ -6455,12 +6465,13 @@ test_write_wifi_wpa_psk_adhoc (void)
 	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
 
 	g_object_set (s_wsec,
-	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none",
+	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
 	              NM_SETTING_WIRELESS_SECURITY_PSK, "7d308b11df1b4243b0f78e5f3fc68cdbb9a264ed0edf4c188edf329ff5b467f0",
 	              NULL);
 
-	nm_setting_wireless_security_add_proto (s_wsec, "wpa");
-	nm_setting_wireless_security_add_group (s_wsec, "tkip");
+	nm_setting_wireless_security_add_proto (s_wsec, "rsn");
+	nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp");
+	nm_setting_wireless_security_add_group (s_wsec, "ccmp");
 
 	/* IP4 setting */
 	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build
index 365ae1a9..0cabe771 100644
--- a/src/settings/plugins/ifupdown/meson.build
+++ b/src/settings/plugins/ifupdown/meson.build
@@ -3,25 +3,18 @@ sources = files(
   'nms-ifupdown-parser.c',
 )
 
-deps = [
-  libudev_dep,
-  nm_dep,
-]
-
 libnms_ifupdown_core = static_library(
   'nms-ifupdown-core',
   sources: sources,
-  dependencies: deps,
-)
-
-sources = files(
-  'nms-ifupdown-plugin.c',
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
 )
 
 libnm_settings_plugin_ifupdown = shared_module(
   'nm-settings-plugin-ifupdown',
-  sources: sources,
-  dependencies: deps,
+  sources: 'nms-ifupdown-plugin.c',
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
   link_with: libnms_ifupdown_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
@@ -31,18 +24,6 @@ libnm_settings_plugin_ifupdown = shared_module(
 
 core_plugins += libnm_settings_plugin_ifupdown
 
-# FIXME: check_so_symbols replacement
-'''
-run_target(
-  'check-local-symbols-settings-ifupdown',
-  command: [check_so_symbols, libnm_settings_plugin_ifupdown.full_path()],
-  depends: libnm_settings_plugin_ifupdown,
-)
-
-check-local-symbols-settings-ifupdown: src/settings/plugins/ifupdown/libnm-settings-plugin-ifupdown.la
-  $(call check_so_symbols,$(builddir)/src/settings/plugins/ifupdown/.libs/libnm-settings-plugin-ifupdown.so)
-'''
-
 if enable_tests
   subdir('tests')
 endif
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
index 75f29878..a146ae60 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
@@ -1,22 +1,7 @@
-/* NetworkManager -- Network link manager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Tom Parker <palfrey@tevp.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2004 Tom Parker
+ * Copyright (C) 2004 Tom Parker
  */
 
 #include "nm-default.h"
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h
index 308228a4..af02c7f6 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h
@@ -1,22 +1,7 @@
-/* NetworkManager -- Network link manager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Tom Parker <palfrey@tevp.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2004 Tom Parker
+ * Copyright (C) 2004 Tom Parker
  */
 
 #ifndef _INTERFACE_PARSER_H
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
index 41b20850..1db9ef15 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -1,22 +1,7 @@
-/* NetworkManager system settings service (ifupdown)
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Alexander Sack <asac@ubuntu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 Canonical Ltd.
+ * Copyright (C) 2008 Canonical Ltd.
  */
 
 #include "nm-default.h"
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
index 7569648f..b8761fc3 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
@@ -1,22 +1,7 @@
-/* NetworkManager system settings service (ifupdown)
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Alexander Sack <asac@ubuntu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 Canonical Ltd.
+ * Copyright (C) 2008 Canonical Ltd.
  */
 
 #ifndef __NMS_IFUPDOWN_PARSER_H__
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index e663ab8a..d19db0fd 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -1,23 +1,8 @@
-/* NetworkManager system settings service (ifupdown)
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Alexander Sack <asac@ubuntu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2007,2008 Canonical Ltd.
- * (C) Copyright 2009 - 2011 Red Hat, Inc.
+ * Copyright (C) 2007, 2008 Canonical Ltd.
+ * Copyright (C) 2009 - 2011 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
index 10ea2be4..91e3e43a 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
@@ -1,22 +1,7 @@
-/* NetworkManager system settings service (ifupdown)
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Alexander Sack <asac@ubuntu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 Canonical Ltd.
+ * Copyright (C) 2008 Canonical Ltd.
  */
 
 #ifndef __NMS_IFUPDOWN_PLUGIN_H__
diff --git a/src/settings/plugins/ifupdown/tests/meson.build b/src/settings/plugins/ifupdown/tests/meson.build
index 9b844c75..1ca094b5 100644
--- a/src/settings/plugins/ifupdown/tests/meson.build
+++ b/src/settings/plugins/ifupdown/tests/meson.build
@@ -3,7 +3,8 @@ test_unit = 'test-ifupdown'
 exe = executable(
   test_unit,
   test_unit + '.c',
-  dependencies: test_nm_dep,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
   link_with: libnms_ifupdown_core,
 )
 
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index 4adcf085..6a7b5ecc 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -149,6 +135,9 @@ expected_free (Expected *e)
 	g_free (e);
 }
 
+NM_AUTO_DEFINE_FCN_VOID0 (Expected *, _nm_auto_free_expected, expected_free)
+#define nm_auto_free_expected nm_auto(_nm_auto_free_expected)
+
 static void
 compare_expected_to_ifparser (if_parser *parser, Expected *e)
 {
@@ -226,7 +215,7 @@ init_ifparser_with_file (const char *file)
 static void
 test1_ignore_line_before_first_block (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test1");
 
@@ -238,14 +227,12 @@ test1_ignore_line_before_first_block (void)
 	expected_block_add_key (b, expected_key_new ("inet", "dhcp"));
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test2_wrapped_line (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test2");
 
@@ -254,14 +241,12 @@ test2_wrapped_line (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test3_wrapped_multiline_multiarg (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test3");
 
@@ -274,14 +259,12 @@ test3_wrapped_multiline_multiarg (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test4_allow_auto_is_auto (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test4");
 
@@ -290,14 +273,12 @@ test4_allow_auto_is_auto (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test5_allow_auto_multiarg (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test5");
 
@@ -308,14 +289,12 @@ test5_allow_auto_multiarg (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test6_mixed_whitespace (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test6");
 
@@ -325,8 +304,6 @@ test6_mixed_whitespace (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
@@ -348,7 +325,7 @@ test8_long_line_wrapped (void)
 static void
 test9_wrapped_lines_in_block (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test9");
 
@@ -362,14 +339,12 @@ test9_wrapped_lines_in_block (void)
 	expected_block_add_key (b, expected_key_new ("gateway", "10.250.2.50"));
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test11_complex_wrap (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test11");
 
@@ -380,14 +355,12 @@ test11_complex_wrap (void)
 	expected_block_add_key (b, expected_key_new ("pre-up", "/sbin/ifconfig eth0 up"));
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test12_complex_wrap_split_word (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test12");
 
@@ -398,14 +371,12 @@ test12_complex_wrap_split_word (void)
 	expected_block_add_key (b, expected_key_new ("up", "ifup ppp0=dsl"));
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test13_more_mixed_whitespace (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test13");
 
@@ -415,14 +386,12 @@ test13_more_mixed_whitespace (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test14_mixed_whitespace_block_start (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test14");
 
@@ -438,14 +407,12 @@ test14_mixed_whitespace_block_start (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test15_trailing_space (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test15");
 
@@ -455,23 +422,20 @@ test15_trailing_space (void)
 	expected_add_block (e, b);
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test16_missing_newline (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test16");
 
 	e = expected_new ();
 	expected_add_block (e, expected_block_new ("mapping", "eth0"));
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
+
 static void
 test17_read_static_ipv4 (void)
 {
@@ -578,7 +542,7 @@ test19_read_static_ipv4_plen (void)
 static void
 test20_source_stanza (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test20-source-stanza");
 
@@ -597,14 +561,12 @@ test20_source_stanza (void)
 	expected_block_add_key (b, expected_key_new ("inet", "dhcp"));
 
 	compare_expected_to_ifparser (parser, e);
-
-	expected_free (e);
 }
 
 static void
 test21_source_dir_stanza (void)
 {
-	Expected *e;
+	nm_auto_free_expected Expected *e = NULL;
 	ExpectedBlock *b;
 	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test21-source-dir-stanza");
 
@@ -617,10 +579,34 @@ test21_source_dir_stanza (void)
 	expected_block_add_key (b, expected_key_new ("inet", "dhcp"));
 
 	compare_expected_to_ifparser (parser, e);
+}
+
+static void
+test22_duplicate_stanzas (void)
+{
+	nm_auto_free_expected Expected *e = NULL;
+	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test22-duplicate-stanzas");
+
+	e = expected_new ();
 
-	expected_free (e);
+	b = expected_block_new ("iface", "br10");
+	expected_add_block (e, b);
+	expected_block_add_key (b, expected_key_new ("inet", "manual"));
+	expected_block_add_key (b, expected_key_new ("bridge-ports", "enp6s0.15"));
+	expected_block_add_key (b, expected_key_new ("bridge-stp", "off"));
+	expected_block_add_key (b, expected_key_new ("bridge-maxwait", "0"));
+	expected_block_add_key (b, expected_key_new ("bridge-fd", "0"));
+	b = expected_block_new ("iface", "br10");
+	expected_add_block (e, b);
+	expected_block_add_key (b, expected_key_new ("inet", "auto"));
+	expected_block_add_key (b, expected_key_new ("bridge-ports", "enp6s0.15"));
+
+	compare_expected_to_ifparser (parser, e);
 }
 
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -650,6 +636,7 @@ main (int argc, char **argv)
 	g_test_add_func ("/ifupdate/read_static_ipv4_plen",          test19_read_static_ipv4_plen);
 	g_test_add_func ("/ifupdate/source_stanza",                  test20_source_stanza);
 	g_test_add_func ("/ifupdate/source_dir_stanza",              test21_source_dir_stanza);
+	g_test_add_func ("/ifupdate/test22-duplicate-stanzas",       test22_duplicate_stanzas);
 
 	return g_test_run ();
 }
diff --git a/src/settings/plugins/ifupdown/tests/test22-duplicate-stanzas b/src/settings/plugins/ifupdown/tests/test22-duplicate-stanzas
new file mode 100644
index 00000000..c13c2e7e
--- /dev/null
+++ b/src/settings/plugins/ifupdown/tests/test22-duplicate-stanzas
@@ -0,0 +1,8 @@
+iface br10 inet manual
+    bridge_ports    enp6s0.15
+    bridge_stp      off
+    bridge_maxwait  0
+    bridge_fd       0
+
+iface br10 inet auto
+    bridge_ports enp6s0.15
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index fbe70ef4..fdb88d2a 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 - 2018 Red Hat, Inc.
  */
@@ -369,7 +355,7 @@ _load_file (NMSKeyfilePlugin *self,
 	                              &local);
 	if (!connection) {
 		if (error)
-			g_propagate_error (error, local);
+			g_propagate_error (error, g_steal_pointer (&local));
 		else
 			_LOGW ("load: \"%s\": failed to load connection: %s", full_filename, local->message);
 		return NULL;
@@ -1090,7 +1076,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self,
 	gboolean hard_failure = FALSE;
 	NMSKeyfileStorage *storage;
 	gs_unref_object NMSKeyfileStorage *storage_result = NULL;
-	gboolean nmmeta_success = FALSE;
+	gboolean nmmeta_errno;
 	gs_free char *nmmeta_filename = NULL;
 	NMSKeyfileStorageType storage_type;
 	const char *loaded_path;
@@ -1116,6 +1102,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self,
 			       simulate ? "simulate " : "",
 			       loaded_path ? "write" : "delete",
 			       uuid);
+			nmmeta_errno = 0;
 			hard_failure = TRUE;
 			goto out;
 		}
@@ -1124,29 +1111,30 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self,
 	}
 
 	if (simulate) {
-		nmmeta_success = TRUE;
+		nmmeta_errno = 0;
 		nmmeta_filename = nms_keyfile_nmmeta_filename (dirname, uuid, FALSE);
 	} else {
-		nmmeta_success = nms_keyfile_nmmeta_write (dirname,
-		                                           uuid,
-		                                           loaded_path,
-		                                           FALSE,
-		                                           shadowed_storage,
-		                                           &nmmeta_filename);
+		nmmeta_errno = nms_keyfile_nmmeta_write (dirname,
+		                                         uuid,
+		                                         loaded_path,
+		                                         FALSE,
+		                                         shadowed_storage,
+		                                         &nmmeta_filename);
 	}
 
-	_LOGT ("commit: %s nmmeta file \"%s\"%s%s%s%s%s%s %s",
+	_LOGT ("commit: %s nmmeta file \"%s\"%s%s%s%s%s%s %s%s%s%s",
 	       loaded_path ? "writing" : "deleting",
 	       nmmeta_filename,
 	       NM_PRINT_FMT_QUOTED (loaded_path, " (pointing to \"", loaded_path, "\")", ""),
 	       NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, "\")", ""),
 	       simulate
 	       ? "simulated"
-	       : (  nmmeta_success
-	          ? "succeeded"
-	          : "failed"));
+	       : (  nmmeta_errno < 0
+	          ? "failed"
+	          : "succeeded"),
+	       NM_PRINT_FMT_QUOTED (nmmeta_errno < 0, " (", nm_strerror_native (nm_errno_native (nmmeta_errno)), ")", ""));
 
-	if (!nmmeta_success)
+	if (nmmeta_errno < 0)
 		goto out;
 
 	storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, nmmeta_filename);
@@ -1177,12 +1165,13 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self,
 	}
 
 out:
-	nm_assert (!nmmeta_success || !hard_failure);
-	nm_assert (nmmeta_success  || !storage_result);
+	nm_assert (nmmeta_errno <= 0);
+	nm_assert (nmmeta_errno < 0  || !hard_failure);
+	nm_assert (nmmeta_errno == 0 || !storage_result);
 
 	NM_SET_OUT (out_hard_failure, hard_failure);
 	NM_SET_OUT (out_storage, (NMSettingsStorage *) g_steal_pointer (&storage_result));
-	return nmmeta_success;
+	return nmmeta_errno >= 0;
 }
 
 /*****************************************************************************/
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/settings/plugins/keyfile/nms-keyfile-plugin.h
index 48440964..e885f16c 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 8d1f5599..af9e6726 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h
index f20e6d93..307d6ffe 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 Red Hat, Inc.
  */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c
index d68d60c8..bcc06795 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-storage.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h
index 2252b47b..00a034d4 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-storage.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c
index ea03e1b6..f03c601a 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -1,20 +1,6 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2010 - 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2010 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -206,7 +192,7 @@ nms_keyfile_nmmeta_read_from_file (const char *full_filename,
 	return TRUE;
 }
 
-gboolean
+int
 nms_keyfile_nmmeta_write (const char *dirname,
                           const char *uuid,
                           const char *loaded_path,
@@ -216,6 +202,7 @@ nms_keyfile_nmmeta_write (const char *dirname,
 {
 	gs_free char *full_filename_tmp = NULL;
 	gs_free char *full_filename = NULL;
+	int errsv;
 
 	nm_assert (dirname && dirname[0] == '/');
 	nm_assert (   nm_utils_is_uuid (uuid)
@@ -231,13 +218,15 @@ nms_keyfile_nmmeta_write (const char *dirname,
 	(void) unlink (full_filename_tmp);
 
 	if (!loaded_path) {
-		gboolean success = TRUE;
-
 		full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0';
-		if (unlink (full_filename_tmp) != 0)
-			success = NM_IN_SET (errno, ENOENT);
+		errsv = 0;
+		if (unlink (full_filename_tmp) != 0) {
+			errsv = -NM_ERRNO_NATIVE (errno);
+			if (errsv == -ENOENT)
+				errsv = 0;
+		}
 		NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp));
-		return success;
+		return errsv;
 	}
 
 	if (loaded_path_allow_relative) {
@@ -266,29 +255,36 @@ nms_keyfile_nmmeta_write (const char *dirname,
 
 		contents = g_key_file_to_data (kf, &length, NULL);
 
-		if (!nm_utils_file_set_contents (full_filename, contents, length, 0600, NULL)) {
+		if (!nm_utils_file_set_contents (full_filename,
+		                                 contents,
+		                                 length,
+		                                 0600,
+		                                 &errsv,
+		                                 NULL)) {
 			NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp));
-			return FALSE;
+			return -NM_ERRNO_NATIVE (errsv);
 		}
 	} else {
 		/* we only have the "loaded_path" to store. That is commonly used for the tombstones to
 		 * link to /dev/null. A symlink is sufficient to store that ammount of information.
 		 * No need to bother with a keyfile. */
 		if (symlink (loaded_path, full_filename_tmp) != 0) {
+			errsv = -NM_ERRNO_NATIVE (errno);
 			full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0';
 			NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp));
-			return FALSE;
+			return errsv;
 		}
 
 		if (rename (full_filename_tmp, full_filename) != 0) {
+			errsv = -NM_ERRNO_NATIVE (errno);
 			(void) unlink (full_filename_tmp);
 			NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename));
-			return FALSE;
+			return errsv;
 		}
 	}
 
 	NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename));
-	return TRUE;
+	return 0;
 }
 
 /*****************************************************************************/
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h
index 723c4436..f943d65c 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h
@@ -1,20 +1,6 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2010 - 2018 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2010 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NMS_KEYFILE_UTILS_H__
@@ -66,12 +52,12 @@ gboolean nms_keyfile_nmmeta_read_from_file (const char *full_filename,
                                             char **out_loaded_path,
                                             char **out_shadowed_storage);
 
-gboolean nms_keyfile_nmmeta_write (const char *dirname,
-                                   const char *uuid,
-                                   const char *loaded_path,
-                                   gboolean loaded_path_allow_relative,
-                                   const char *shadowed_storage,
-                                   char **out_full_filename);
+int nms_keyfile_nmmeta_write (const char *dirname,
+                              const char *uuid,
+                              const char *loaded_path,
+                              gboolean loaded_path_allow_relative,
+                              const char *shadowed_storage,
+                              char **out_full_filename);
 
 /*****************************************************************************/
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index abd3f1f4..fa95198c 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 - 2015 Red Hat, Inc.
  */
@@ -130,8 +116,12 @@ cert_writer (NMConnection *connection,
 		 * being sure that the entire profile can be written and all circumstances are good to
 		 * proceed. That means, while writing we must only collect the blogs in-memory, and write
 		 * them all in the end together (or not at all). */
-		success = nm_utils_file_set_contents (new_path, (const char *) blob_data,
-		                                      blob_len, 0600, &local);
+		success = nm_utils_file_set_contents (new_path,
+		                                      (const char *) blob_data,
+		                                      blob_len,
+		                                      0600,
+		                                      NULL,
+		                                      &local);
 		if (success) {
 			/* Write the path value to the keyfile.
 			 * We know, that basename(new_path) starts with a UUID, hence no conflict with "data:;base64,"  */
@@ -344,7 +334,12 @@ _internal_write_connection (NMConnection *connection,
 		}
 	}
 
-	nm_utils_file_set_contents (path, kf_content_buf, kf_content_len, 0600, &local_err);
+	nm_utils_file_set_contents (path,
+	                            kf_content_buf,
+	                            kf_content_len,
+	                            0600,
+	                            NULL,
+	                            &local_err);
 	if (local_err) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "error writing to file '%s': %s",
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h
index 99e86025..98ec8a6b 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build
index f1e96bdf..7bf9fda0 100644
--- a/src/settings/plugins/keyfile/tests/meson.build
+++ b/src/settings/plugins/keyfile/tests/meson.build
@@ -3,7 +3,8 @@ test_unit = 'test-keyfile-settings'
 exe = executable(
   test_unit,
   test_unit + '.c',
-  dependencies: test_nm_dep,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
 )
 
 test(
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
index f96111a2..d2da09da 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -1,19 +1,5 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2017 Red Hat, Inc.
  */
 
@@ -2543,7 +2529,7 @@ _assert_keyfile_nmmeta (const char *dirname,
 	nm_clear_g_free (&full_filename);
 
 
-	g_assert (nms_keyfile_nmmeta_write (dirname, uuid, loaded_path, allow_relative, NULL, &full_filename));
+	g_assert_cmpint (nms_keyfile_nmmeta_write (dirname, uuid, loaded_path, allow_relative, NULL, &full_filename), ==, 0);
 	g_assert_cmpstr (full_filename, ==, exp_full_filename);
 	nm_clear_g_free (&full_filename);
 
diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c
index a5a68070..dec4556d 100644
--- a/src/supplicant/nm-supplicant-config.c
+++ b/src/supplicant/nm-supplicant-config.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2012 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
@@ -598,20 +584,20 @@ nm_supplicant_config_add_bgscan (NMSupplicantConfig *self,
 	 * (b) since EAP/802.1x isn't used and thus there are fewer steps to fail
 	 * during a roam, we can wait longer before scanning for roam candidates.
 	 */
-	bgscan = "simple:30:-80:86400";
+	bgscan = "simple:30:-70:86400";
 
-	/* If using WPA Enterprise or Dynamic WEP use a shorter bgscan interval on
-	 * the assumption that this is a multi-AP ESS in which we want more reliable
-	 * roaming between APs.  Thus trigger scans when the signal is still somewhat
-	 * OK so we have an up-to-date roam candidate list when the signal gets bad.
+	/* If using WPA Enterprise, Dynamic WEP or we have seen more than one AP use
+	 * a shorter bgscan interval on the assumption that this is a multi-AP ESS
+	 * in which we want more reliable roaming between APs. Thus trigger scans
+	 * when the signal is still somewhat OK so we have an up-to-date roam
+	 * candidate list when the signal gets bad.
 	 */
-	s_wsec = nm_connection_get_setting_wireless_security (connection);
-	if (s_wsec) {
-		if (NM_IN_STRSET (nm_setting_wireless_security_get_key_mgmt (s_wsec),
-		                  "ieee8021x",
-		                  "wpa-eap"))
-			bgscan = "simple:30:-65:300";
-	}
+	if (   nm_setting_wireless_get_num_seen_bssids (s_wifi) > 1
+	    || (   (s_wsec = nm_connection_get_setting_wireless_security (connection))
+	        && NM_IN_STRSET (nm_setting_wireless_security_get_key_mgmt (s_wsec),
+	                         "ieee8021x",
+	                         "wpa-eap")))
+		bgscan = "simple:30:-65:300";
 
 	return nm_supplicant_config_add_option (self, "bgscan", bgscan, -1, FALSE, error);
 }
@@ -897,8 +883,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
 	}
 
 	/* Only WPA-specific things when using WPA */
-	if (   !strcmp (key_mgmt, "wpa-none")
-	    || !strcmp (key_mgmt, "wpa-psk")
+	if (   !strcmp (key_mgmt, "wpa-psk")
 	    || !strcmp (key_mgmt, "wpa-eap")
 	    || !strcmp (key_mgmt, "sae")) {
 		if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, proto, protos, "proto", ' ', TRUE, NULL, error))
@@ -909,7 +894,6 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
 			return FALSE;
 
 		if (   set_pmf
-		    && !nm_streq (key_mgmt, "wpa-none")
 		    && NM_IN_SET (pmf,
 		                  NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE,
 		                  NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)) {
diff --git a/src/supplicant/nm-supplicant-config.h b/src/supplicant/nm-supplicant-config.h
index c4e7310d..361f9ac6 100644
--- a/src/supplicant/nm-supplicant-config.h
+++ b/src/supplicant/nm-supplicant-config.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2012 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
index be4f65dc..6ef29311 100644
--- a/src/supplicant/nm-supplicant-interface.c
+++ b/src/supplicant/nm-supplicant-interface.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -1557,7 +1543,7 @@ p2p_props_changed_cb (GDBusProxy *proxy,
 	if (g_variant_lookup (changed_properties, "Group", "&o", &path)) {
 		if (priv->group_proxy && g_strcmp0 (path, g_dbus_proxy_get_object_path (priv->group_proxy)) == 0) {
 			/* We already have the proxy, nothing to do. */
-		} else if (path && g_strcmp0 (path, "/") != 0) {
+		} else if (nm_dbus_path_not_empty (path)) {
 			if (priv->group_proxy != NULL) {
 				_LOGW ("P2P: Unexpected update of the group object path");
 				priv->group_proxy_acquired = FALSE;
diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
index e621f1a3..3580f754 100644
--- a/src/supplicant/nm-supplicant-interface.h
+++ b/src/supplicant/nm-supplicant-interface.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2017 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c
index df641c51..49581f6e 100644
--- a/src/supplicant/nm-supplicant-manager.c
+++ b/src/supplicant/nm-supplicant-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2010 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
diff --git a/src/supplicant/nm-supplicant-manager.h b/src/supplicant/nm-supplicant-manager.h
index a1f23f53..18ca53b6 100644
--- a/src/supplicant/nm-supplicant-manager.h
+++ b/src/supplicant/nm-supplicant-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2008 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c
index 25cc0bc6..bea17ede 100644
--- a/src/supplicant/nm-supplicant-settings-verify.c
+++ b/src/supplicant/nm-supplicant-settings-verify.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2012 Red Hat, Inc.
  */
 
@@ -69,7 +55,7 @@ static const char *const proto_allowed[] =    { "WPA", "RSN", NULL };
 static const char *const key_mgmt_allowed[] = { "WPA-PSK", "WPA-PSK-SHA256", "FT-PSK",
                                                 "WPA-EAP", "WPA-EAP-SHA256", "FT-EAP", "FT-EAP-SHA384",
                                                 "FILS-SHA256", "FILS-SHA384",
-                                                "IEEE8021X", "WPA-NONE", "SAE", "FT-SAE",
+                                                "IEEE8021X", "SAE", "FT-SAE",
                                                 "NONE", NULL };
 static const char *const auth_alg_allowed[] = { "OPEN", "SHARED", "LEAP", NULL };
 static const char *const eap_allowed[] =      { "LEAP", "MD5", "TLS", "PEAP", "TTLS", "SIM",
diff --git a/src/supplicant/nm-supplicant-settings-verify.h b/src/supplicant/nm-supplicant-settings-verify.h
index ea2482d3..fe1480cc 100644
--- a/src/supplicant/nm-supplicant-settings-verify.h
+++ b/src/supplicant/nm-supplicant-settings-verify.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2008 Red Hat, Inc.
  */
 
diff --git a/src/supplicant/nm-supplicant-types.h b/src/supplicant/nm-supplicant-types.h
index b28a1efa..2b356354 100644
--- a/src/supplicant/nm-supplicant-types.h
+++ b/src/supplicant/nm-supplicant-types.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2006 - 2008 Red Hat, Inc.
  */
 
diff --git a/src/supplicant/tests/meson.build b/src/supplicant/tests/meson.build
index fbccb313..207d22f8 100644
--- a/src/supplicant/tests/meson.build
+++ b/src/supplicant/tests/meson.build
@@ -3,7 +3,8 @@ test_unit = 'test-supplicant-config'
 exe = executable(
   test_unit,
   test_unit + '.c',
-  dependencies: test_nm_dep,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
 )
 
 test(
diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c
index ea9bac1e..008735b4 100644
--- a/src/supplicant/tests/test-supplicant-config.c
+++ b/src/supplicant/tests/test-supplicant-config.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
 
@@ -236,7 +222,7 @@ test_wifi_wep_key (const char *detail,
 	gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data));
 	const char *bssid_str = "11:22:33:44:55:66";
 	gs_unref_bytes GBytes *wep_key_bytes = g_bytes_new (expected, expected_size);
-	const char *bgscan_data = "simple:30:-80:86400";
+	const char *bgscan_data = "simple:30:-70:86400";
 	gs_unref_bytes GBytes *bgscan = g_bytes_new (bgscan_data, strlen (bgscan_data));
 
 	connection = new_basic_connection ("Test Wifi WEP Key", ssid, test_bssid ? bssid_str : NULL);
@@ -264,7 +250,7 @@ test_wifi_wep_key (const char *detail,
 	NMTST_EXPECT_NM_INFO ("Config: added 'wep_key0' value *");
 	NMTST_EXPECT_NM_INFO ("Config: added 'wep_tx_keyidx' value '0'");
 	if (!test_bssid)
-		NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-80:86400'*");
+		NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-70:86400'*");
 
 	config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE);
 	g_test_assert_expected_messages ();
diff --git a/src/systemd/meson.build b/src/systemd/meson.build
index af1d0c8b..5c4fa3e2 100644
--- a/src/systemd/meson.build
+++ b/src/systemd/meson.build
@@ -1,45 +1,48 @@
+sources = files(
+  'src/libsystemd-network/arp-util.c',
+  'src/libsystemd-network/dhcp-identifier.c',
+  'src/libsystemd-network/dhcp-network.c',
+  'src/libsystemd-network/dhcp-option.c',
+  'src/libsystemd-network/dhcp-packet.c',
+  'src/libsystemd-network/dhcp6-network.c',
+  'src/libsystemd-network/dhcp6-option.c',
+  'src/libsystemd-network/lldp-neighbor.c',
+  'src/libsystemd-network/lldp-network.c',
+  'src/libsystemd-network/network-internal.c',
+  'src/libsystemd-network/sd-dhcp-client.c',
+  'src/libsystemd-network/sd-dhcp-lease.c',
+  'src/libsystemd-network/sd-dhcp6-client.c',
+  'src/libsystemd-network/sd-dhcp6-lease.c',
+  'src/libsystemd-network/sd-ipv4acd.c',
+  'src/libsystemd-network/sd-ipv4ll.c',
+  'src/libsystemd-network/sd-lldp.c',
+  'src/libsystemd/sd-event/event-util.c',
+  'src/libsystemd/sd-event/sd-event.c',
+  'src/libsystemd/sd-id128/id128-util.c',
+  'src/libsystemd/sd-id128/sd-id128.c',
+  'nm-sd.c',
+  'nm-sd-utils-core.c',
+  'nm-sd-utils-dhcp.c',
+  'sd-adapt-core/nm-sd-adapt-core.c',
+)
+
+incs = include_directories(
+  'sd-adapt-core',
+  'src/libsystemd-network',
+  'src/libsystemd/sd-event',
+  'src/systemd',
+)
+
+deps = [
+  daemon_nm_default_dep,
+  libnm_systemd_shared_dep,
+]
+
 libnm_systemd_core = static_library(
   'nm-systemd-core',
-  sources: files(
-    'sd-adapt-core/nm-sd-adapt-core.c',
-    'src/libsystemd-network/arp-util.c',
-    'src/libsystemd-network/dhcp-identifier.c',
-    'src/libsystemd-network/dhcp-network.c',
-    'src/libsystemd-network/dhcp-option.c',
-    'src/libsystemd-network/dhcp-packet.c',
-    'src/libsystemd-network/dhcp6-network.c',
-    'src/libsystemd-network/dhcp6-option.c',
-    'src/libsystemd-network/lldp-neighbor.c',
-    'src/libsystemd-network/lldp-network.c',
-    'src/libsystemd-network/network-internal.c',
-    'src/libsystemd-network/sd-dhcp-client.c',
-    'src/libsystemd-network/sd-dhcp-lease.c',
-    'src/libsystemd-network/sd-dhcp6-client.c',
-    'src/libsystemd-network/sd-dhcp6-lease.c',
-    'src/libsystemd-network/sd-ipv4acd.c',
-    'src/libsystemd-network/sd-ipv4ll.c',
-    'src/libsystemd-network/sd-lldp.c',
-    'src/libsystemd/sd-event/event-util.c',
-    'src/libsystemd/sd-event/sd-event.c',
-    'src/libsystemd/sd-id128/id128-util.c',
-    'src/libsystemd/sd-id128/sd-id128.c',
-    'nm-sd.c',
-    'nm-sd-utils-core.c',
-    'nm-sd-utils-dhcp.c',
-  ),
-  include_directories: [
-    src_inc,
-    include_directories(
-      'sd-adapt-core',
-      'src/libsystemd-network',
-      'src/libsystemd/sd-event',
-      'src/systemd',
-    )
-  ],
-  dependencies: [
-    libnm_core_dep,
-  ],
-  c_args: [
-    '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD',
-  ],
+  sources: sources,
+  include_directories: incs,
+  dependencies: deps,
+  c_args: '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD',
+  link_with: libc_siphash,
 )
diff --git a/src/systemd/nm-sd-utils-core.c b/src/systemd/nm-sd-utils-core.c
index 42560789..4057c70a 100644
--- a/src/systemd/nm-sd-utils-core.c
+++ b/src/systemd/nm-sd-utils-core.c
@@ -1,18 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/systemd/nm-sd-utils-core.h b/src/systemd/nm-sd-utils-core.h
index a7b092b3..3b0bc23b 100644
--- a/src/systemd/nm-sd-utils-core.h
+++ b/src/systemd/nm-sd-utils-core.h
@@ -1,18 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
diff --git a/src/systemd/nm-sd-utils-dhcp.c b/src/systemd/nm-sd-utils-dhcp.c
index 49a924b6..4cfe054c 100644
--- a/src/systemd/nm-sd-utils-dhcp.c
+++ b/src/systemd/nm-sd-utils-dhcp.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/systemd/nm-sd-utils-dhcp.h b/src/systemd/nm-sd-utils-dhcp.h
index 496bf374..5228eb25 100644
--- a/src/systemd/nm-sd-utils-dhcp.h
+++ b/src/systemd/nm-sd-utils-dhcp.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DHCP_SYSTEMD_UTILS_H__
diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c
index bcab328f..a277f430 100644
--- a/src/systemd/nm-sd.c
+++ b/src/systemd/nm-sd.c
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 - 2016 Red Hat, Inc.
  */
 
@@ -30,7 +18,6 @@ typedef struct SDEventSource {
 	GSource source;
 	GPollFD pollfd;
 	sd_event *event;
-	guint *default_source_id;
 } SDEventSource;
 
 static gboolean
@@ -48,41 +35,49 @@ event_check (GSource *source)
 static gboolean
 event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
 {
-	return sd_event_dispatch (((SDEventSource *)source)->event) > 0;
+	return sd_event_dispatch (((SDEventSource *) source)->event) > 0;
 }
 
 static void
 event_finalize (GSource *source)
 {
-	SDEventSource *s;
+	SDEventSource *s = (SDEventSource *) source;
 
-	s = (SDEventSource *) source;
 	sd_event_unref (s->event);
-	if (s->default_source_id)
-		*s->default_source_id = 0;
 }
 
 static SDEventSource *
-event_create_source (sd_event *event, guint *default_source_id)
+event_create_source (sd_event *event)
 {
-	static GSourceFuncs event_funcs = {
+	static const GSourceFuncs event_funcs = {
 		.prepare = event_prepare,
 		.check = event_check,
 		.dispatch = event_dispatch,
 		.finalize = event_finalize,
 	};
 	SDEventSource *source;
+	gboolean is_default_event = FALSE;
+	int r;
 
-	g_return_val_if_fail (event, NULL);
+	if (!event) {
+		is_default_event = TRUE;
+		r = sd_event_default (&event);
+		if (r < 0)
+			g_return_val_if_reached (NULL);
+	}
 
-	source = (SDEventSource *) g_source_new (&event_funcs, sizeof (SDEventSource));
+	source = (SDEventSource *) g_source_new ((GSourceFuncs *) &event_funcs, sizeof (SDEventSource));
 
-	source->event = sd_event_ref (event);
-	source->pollfd.fd = sd_event_get_fd (event);
-	source->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
-	source->default_source_id = default_source_id;
+	source->event =   is_default_event
+	                ? g_steal_pointer (&event)
+	                : sd_event_ref (event);
 
-	g_source_add_poll ((GSource *) source, &source->pollfd);
+	source->pollfd = (GPollFD) {
+	    .fd     = sd_event_get_fd (source->event),
+	    .events = G_IO_IN | G_IO_HUP | G_IO_ERR,
+	};
+
+	g_source_add_poll (&source->source, &source->pollfd);
 
 	return source;
 }
@@ -92,35 +87,15 @@ event_attach (sd_event *event, GMainContext *context)
 {
 	SDEventSource *source;
 	guint id;
-	int r;
-	sd_event *e = event;
-	guint *p_default_source_id = NULL;
 
-	if (!e) {
-		static guint default_source_id = 0;
+	source = event_create_source (event);
 
-		if (default_source_id) {
-			/* The default event cannot be registered multiple times. */
-			g_return_val_if_reached (0);
-		}
+	g_return_val_if_fail (source, 0);
 
-		r = sd_event_default (&e);
-		if (r < 0)
-			g_return_val_if_reached (0);
-
-		p_default_source_id = &default_source_id;
-	}
-
-	source = event_create_source (e, p_default_source_id);
 	id = g_source_attach ((GSource *) source, context);
 	g_source_unref ((GSource *) source);
 
-	if (!event) {
-		*p_default_source_id = id;
-		sd_event_unref (e);
-	}
-
-	g_return_val_if_fail (id, 0);
+	nm_assert (id != 0);
 	return id;
 }
 
diff --git a/src/systemd/nm-sd.h b/src/systemd/nm-sd.h
index 07fc2ad4..99904243 100644
--- a/src/systemd/nm-sd.h
+++ b/src/systemd/nm-sd.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 - 2016 Red Hat, Inc.
  */
 
diff --git a/src/systemd/sd-adapt-core/nm-sd-adapt-core.c b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c
index c461752e..a9f8bd1c 100644
--- a/src/systemd/sd-adapt-core/nm-sd-adapt-core.c
+++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 - 2016 Red Hat, Inc.
  */
 
diff --git a/src/systemd/sd-adapt-core/nm-sd-adapt-core.h b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h
index bd71dd18..81bc7320 100644
--- a/src/systemd/sd-adapt-core/nm-sd-adapt-core.h
+++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h
@@ -1,18 +1,5 @@
-/* This program 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.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2014 - 2018 Red Hat, Inc.
  */
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h
index e0269b54..6a803d7b 100644
--- a/src/systemd/src/libsystemd-network/dhcp-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-internal.h
@@ -15,11 +15,21 @@
 #include "dhcp-protocol.h"
 #include "socket-util.h"
 
+typedef struct sd_dhcp_option {
+        unsigned n_ref;
+
+        uint8_t option;
+        void *data;
+        size_t length;
+} sd_dhcp_option;
+
+extern const struct hash_ops dhcp_option_hash_ops;
+
 int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
                                  uint32_t xid, const uint8_t *mac_addr,
                                  size_t mac_addr_len, uint16_t arp_type,
                                  uint16_t port);
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port);
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type);
 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
                                  const void *packet, size_t len);
 int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
@@ -41,7 +51,7 @@ uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
 
 void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
                                    uint16_t source, be32_t destination_addr,
-                                   uint16_t destination, uint16_t len);
+                                   uint16_t destination, uint16_t len, int ip_service_type);
 
 int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port);
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
index 122042ab..a2d0f8bd 100644
--- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
@@ -58,6 +58,9 @@ struct sd_dhcp_lease {
         struct in_addr *ntp;
         size_t ntp_size;
 
+        struct in_addr *sip;
+        size_t sip_size;
+
         struct sd_dhcp_route *static_route;
         size_t static_route_size, static_route_allocated;
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c
index 858fe57c..1f01ece1 100644
--- a/src/systemd/src/libsystemd-network/dhcp-network.c
+++ b/src/systemd/src/libsystemd-network/dhcp-network.c
@@ -11,7 +11,6 @@
 #include <net/if_arp.h>
 #include <stdio.h>
 #include <string.h>
-#include <sys/socket.h>
 #include <linux/filter.h>
 #include <linux/if_infiniband.h>
 #include <linux/if_packet.h>
@@ -148,7 +147,7 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
                                 bcast_addr, &eth_mac, arp_type, dhcp_hlen, port);
 }
 
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
         union sockaddr_union src = {
                 .in.sin_family = AF_INET,
                 .in.sin_port = htobe16(port),
@@ -161,7 +160,11 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
         if (s < 0)
                 return -errno;
 
-        r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
+        if (ip_service_type >= 0)
+                r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type);
+        else
+                r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
+
         if (r < 0)
                 return r;
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c
index 50238b0a..49193879 100644
--- a/src/systemd/src/libsystemd-network/dhcp-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp-option.c
@@ -8,10 +8,10 @@
 #include <errno.h>
 #include <stdint.h>
 #include <stdio.h>
-#include <string.h>
 
 #include "alloc-util.h"
 #include "dhcp-internal.h"
+#include "dhcp-server-internal.h"
 #include "memory-util.h"
 #include "strv.h"
 #include "utf8.h"
@@ -29,7 +29,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
 
         case SD_DHCP_OPTION_PAD:
         case SD_DHCP_OPTION_END:
-                if (size < *offset + 1)
+                if (*offset + 1 > size)
                         return -ENOBUFS;
 
                 options[*offset] = code;
@@ -37,42 +37,83 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
                 break;
 
         case SD_DHCP_OPTION_USER_CLASS: {
-                size_t len = 0;
+                size_t total = 0;
                 char **s;
 
-                STRV_FOREACH(s, (char **) optval)
-                        len += strlen(*s) + 1;
+                STRV_FOREACH(s, (char **) optval) {
+                        size_t len = strlen(*s);
+
+                        if (len > 255)
+                                return -ENAMETOOLONG;
+
+                        total += 1 + len;
+                }
 
-                if (size < *offset + len + 2)
+                if (*offset + 2 + total > size)
                         return -ENOBUFS;
 
                 options[*offset] = code;
-                options[*offset + 1] =  len;
+                options[*offset + 1] =  total;
                 *offset += 2;
 
                 STRV_FOREACH(s, (char **) optval) {
-                        len = strlen(*s);
-
-                        if (len > 255)
-                                return -ENAMETOOLONG;
+                        size_t len = strlen(*s);
 
                         options[*offset] = len;
 
-                        memcpy_safe(&options[*offset + 1], *s, len);
-                        *offset += len + 1;
+                        memcpy(&options[*offset + 1], *s, len);
+                        *offset += 1 + len;
+                }
+
+                break;
+        }
+        case SD_DHCP_OPTION_SIP_SERVER:
+                if (*offset + 3 + optlen > size)
+                        return -ENOBUFS;
+
+                options[*offset] = code;
+                options[*offset + 1] = optlen + 1;
+                options[*offset + 2] = 1;
+
+                memcpy_safe(&options[*offset + 3], optval, optlen);
+                *offset += 3 + optlen;
+
+                break;
+        case SD_DHCP_OPTION_VENDOR_SPECIFIC: {
+                OrderedHashmap *s = (OrderedHashmap *) optval;
+                struct sd_dhcp_option *p;
+                size_t l = 0;
+                Iterator i;
+
+                ORDERED_HASHMAP_FOREACH(p, s, i)
+                        l += p->length + 2;
+
+                if (*offset + l + 2 > size)
+                        return -ENOBUFS;
+
+                options[*offset] = code;
+                options[*offset + 1] = l;
+
+                *offset += 2;
+
+                ORDERED_HASHMAP_FOREACH(p, s, i) {
+                        options[*offset] = p->option;
+                        options[*offset + 1] = p->length;
+                        memcpy(&options[*offset + 2], p->data, p->length);
+                        *offset += 2 + p->length;
                 }
 
                 break;
         }
         default:
-                if (size < *offset + optlen + 2)
+                if (*offset + 2 + optlen > size)
                         return -ENOBUFS;
 
                 options[*offset] = code;
                 options[*offset + 1] = optlen;
 
                 memcpy_safe(&options[*offset + 2], optval, optlen);
-                *offset += optlen + 2;
+                *offset += 2 + optlen;
 
                 break;
         }
@@ -83,22 +124,25 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
 int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
                        uint8_t overload,
                        uint8_t code, size_t optlen, const void *optval) {
-        size_t file_offset = 0, sname_offset =0;
-        bool file, sname;
+        const bool use_file = overload & DHCP_OVERLOAD_FILE;
+        const bool use_sname = overload & DHCP_OVERLOAD_SNAME;
         int r;
 
         assert(message);
         assert(offset);
 
-        file = overload & DHCP_OVERLOAD_FILE;
-        sname = overload & DHCP_OVERLOAD_SNAME;
+        /* If *offset is in range [0, size), we are writing to ->options,
+         * if *offset is in range [size, size + sizeof(message->file)) and use_file, we are writing to ->file,
+         * if *offset is in range [size + use_file*sizeof(message->file), size + use_file*sizeof(message->file) + sizeof(message->sname))
+         * and use_sname, we are writing to ->sname.
+         */
 
         if (*offset < size) {
                 /* still space in the options array */
                 r = option_append(message->options, size, offset, code, optlen, optval);
                 if (r >= 0)
                         return 0;
-                else if (r == -ENOBUFS && (file || sname)) {
+                else if (r == -ENOBUFS && (use_file || use_sname)) {
                         /* did not fit, but we have more buffers to try
                            close the options array and move the offset to its end */
                         r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
@@ -110,8 +154,8 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
                         return r;
         }
 
-        if (overload & DHCP_OVERLOAD_FILE) {
-                file_offset = *offset - size;
+        if (use_file) {
+                size_t file_offset = *offset - size;
 
                 if (file_offset < sizeof(message->file)) {
                         /* still space in the 'file' array */
@@ -119,7 +163,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
                         if (r >= 0) {
                                 *offset = size + file_offset;
                                 return 0;
-                        } else if (r == -ENOBUFS && sname) {
+                        } else if (r == -ENOBUFS && use_sname) {
                                 /* did not fit, but we have more buffers to try
                                    close the file array and move the offset to its end */
                                 r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
@@ -132,19 +176,18 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
                 }
         }
 
-        if (overload & DHCP_OVERLOAD_SNAME) {
-                sname_offset = *offset - size - (file ? sizeof(message->file) : 0);
+        if (use_sname) {
+                size_t sname_offset = *offset - size - use_file*sizeof(message->file);
 
                 if (sname_offset < sizeof(message->sname)) {
                         /* still space in the 'sname' array */
                         r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval);
                         if (r >= 0) {
-                                *offset = size + (file ? sizeof(message->file) : 0) + sname_offset;
+                                *offset = size + use_file*sizeof(message->file) + sname_offset;
                                 return 0;
-                        } else {
+                        } else
                                 /* no space, or other error, give up */
                                 return r;
-                        }
                 }
         }
 
@@ -274,3 +317,43 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t c
 
         return message_type;
 }
+
+static sd_dhcp_option* dhcp_option_free(sd_dhcp_option *i) {
+        if (!i)
+                return NULL;
+
+        free(i->data);
+        return mfree(i);
+}
+
+int sd_dhcp_option_new(uint8_t option, const void *data, size_t length, sd_dhcp_option **ret) {
+        assert_return(ret, -EINVAL);
+        assert_return(length == 0 || data, -EINVAL);
+
+        _cleanup_free_ void *q = memdup(data, length);
+        if (!q)
+                return -ENOMEM;
+
+        sd_dhcp_option *p = new(sd_dhcp_option, 1);
+        if (!p)
+                return -ENOMEM;
+
+        *p = (sd_dhcp_option) {
+                .n_ref = 1,
+                .option = option,
+                .length = length,
+                .data = TAKE_PTR(q),
+        };
+
+        *ret = TAKE_PTR(p);
+        return 0;
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_option, sd_dhcp_option, dhcp_option_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                dhcp_option_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                sd_dhcp_option,
+                sd_dhcp_option_unref);
diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c
index 9e565e28..2062be91 100644
--- a/src/systemd/src/libsystemd-network/dhcp-packet.c
+++ b/src/systemd/src/libsystemd-network/dhcp-packet.c
@@ -77,12 +77,15 @@ uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
 
 void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
                                    uint16_t source_port, be32_t destination_addr,
-                                   uint16_t destination_port, uint16_t len) {
+                                   uint16_t destination_port, uint16_t len, int ip_service_type) {
         packet->ip.version = IPVERSION;
         packet->ip.ihl = DHCP_IP_SIZE / 4;
         packet->ip.tot_len = htobe16(len);
 
-        packet->ip.tos = IPTOS_CLASS_CS6;
+        if (ip_service_type >= 0)
+                packet->ip.tos = ip_service_type;
+        else
+                packet->ip.tos = IPTOS_CLASS_CS6;
 
         packet->ip.protocol = IPPROTO_UDP;
         packet->ip.saddr = source_addr;
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index f28ba68d..517e357d 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -85,7 +85,7 @@ typedef struct DHCP6IA DHCP6IA;
 int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
                         size_t optlen, const void *optval);
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd);
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
 int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c
index 73c195a7..d43680a5 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-network.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-network.c
@@ -10,7 +10,6 @@
 #include <netinet/ip6.h>
 #include <stdio.h>
 #include <string.h>
-#include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <linux/if_packet.h>
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index 562a34b5..bb4c4d91 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -7,7 +7,6 @@
 
 #include <errno.h>
 #include <netinet/in.h>
-#include <string.h>
 
 #include "sd-dhcp6-client.h"
 
@@ -170,9 +169,10 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
         return r;
 }
 
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) {
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
         DHCP6Option *option = (DHCP6Option *)buf;
         size_t i = sizeof(*option) + sizeof(pd->ia_pd);
+        DHCP6PDPrefixOption *prefix_opt;
         DHCP6Address *prefix;
 
         assert_return(buf, -EINVAL);
@@ -185,10 +185,7 @@ int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) {
         option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
 
         memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
-
         LIST_FOREACH(addresses, prefix, pd->addresses) {
-                DHCP6PDPrefixOption *prefix_opt;
-
                 if (len < i + sizeof(*prefix_opt))
                         return -ENOBUFS;
 
@@ -196,9 +193,19 @@ int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) {
                 prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
                 prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
 
-                memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
-                       sizeof(struct iapdprefix));
+                memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, sizeof(struct iapdprefix));
+                i += sizeof(*prefix_opt);
+        }
+
+        if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
+                if (len < i + sizeof(*prefix_opt))
+                        return -ENOBUFS;
+
+                prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
+                prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
+                prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
 
+                memcpy(&prefix_opt->iapdprefix, &hint_pd_prefix->iapdprefix, sizeof(struct iapdprefix));
                 i += sizeof(*prefix_opt);
         }
 
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c
index 530af034..238d718b 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.c
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c
@@ -10,7 +10,7 @@
 #include "lldp-internal.h"
 #include "lldp-neighbor.h"
 #include "memory-util.h"
-#include "missing.h"
+#include "missing_network.h"
 #include "unaligned.h"
 
 static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c
index 5ba9f081..f4764dd1 100644
--- a/src/systemd/src/libsystemd-network/lldp-network.c
+++ b/src/systemd/src/libsystemd-network/lldp-network.c
@@ -7,7 +7,7 @@
 
 #include "fd-util.h"
 #include "lldp-network.h"
-#include "missing.h"
+#include "missing_network.h"
 #include "socket-util.h"
 
 int lldp_network_bind_raw_socket(int ifindex) {
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index 209ce884..7d22e754 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -22,6 +22,7 @@
 #include "parse-util.h"
 #include "siphash24.h"
 #include "socket-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "utf8.h"
@@ -139,15 +140,38 @@ static int net_condition_test_property(char * const *match_property, sd_device *
         return true;
 }
 
+static const char *const wifi_iftype_table[NL80211_IFTYPE_MAX+1] = {
+        [NL80211_IFTYPE_ADHOC] = "ad-hoc",
+        [NL80211_IFTYPE_STATION] = "station",
+        [NL80211_IFTYPE_AP] = "ap",
+        [NL80211_IFTYPE_AP_VLAN] = "ap-vlan",
+        [NL80211_IFTYPE_WDS] = "wds",
+        [NL80211_IFTYPE_MONITOR] = "monitor",
+        [NL80211_IFTYPE_MESH_POINT] = "mesh-point",
+        [NL80211_IFTYPE_P2P_CLIENT] = "p2p-client",
+        [NL80211_IFTYPE_P2P_GO] = "p2p-go",
+        [NL80211_IFTYPE_P2P_DEVICE] = "p2p-device",
+        [NL80211_IFTYPE_OCB] = "ocb",
+        [NL80211_IFTYPE_NAN] = "nan",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wifi_iftype, enum nl80211_iftype);
+
 bool net_match_config(Set *match_mac,
                       char * const *match_paths,
                       char * const *match_drivers,
                       char * const *match_types,
                       char * const *match_names,
                       char * const *match_property,
+                      char * const *match_wifi_iftype,
+                      char * const *match_ssid,
+                      Set *match_bssid,
                       sd_device *device,
                       const struct ether_addr *dev_mac,
-                      const char *dev_name) {
+                      const char *dev_name,
+                      enum nl80211_iftype wifi_iftype,
+                      const char *ssid,
+                      const struct ether_addr *bssid) {
 
         const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
 
@@ -181,6 +205,15 @@ bool net_match_config(Set *match_mac,
         if (!net_condition_test_property(match_property, device))
                 return false;
 
+        if (!net_condition_test_strv(match_wifi_iftype, wifi_iftype_to_string(wifi_iftype)))
+                return false;
+
+        if (!net_condition_test_strv(match_ssid, ssid))
+                return false;
+
+        if (match_bssid && (!bssid || !set_contains(match_bssid, bssid)))
+                return false;
+
         return true;
 }
 
@@ -257,7 +290,7 @@ int config_parse_match_strv(
         for (;;) {
                 _cleanup_free_ char *word = NULL, *k = NULL;
 
-                r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+                r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
                 if (r == 0)
                         return 0;
                 if (r == -ENOMEM)
diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h
index 487421fb..2eb0cba5 100644
--- a/src/systemd/src/libsystemd-network/network-internal.h
+++ b/src/systemd/src/libsystemd-network/network-internal.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include <linux/nl80211.h>
 #include <stdbool.h>
 
 #include "sd-device.h"
@@ -21,9 +22,15 @@ bool net_match_config(Set *match_mac,
                       char * const *match_type,
                       char * const *match_name,
                       char * const *match_property,
+                      char * const *match_wifi_iftype,
+                      char * const *match_ssid,
+                      Set *match_bssid,
                       sd_device *device,
                       const struct ether_addr *dev_mac,
-                      const char *dev_name);
+                      const char *dev_name,
+                      enum nl80211_iftype wifi_iftype,
+                      const char *ssid,
+                      const struct ether_addr *bssid);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_net_condition);
 CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index 2f531bc7..0266161d 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -10,7 +10,6 @@
 #include <net/if_arp.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/ioctl.h>
 #include <linux/if_infiniband.h>
 
@@ -92,6 +91,7 @@ struct sd_dhcp_client {
         usec_t start_time;
         uint64_t attempt;
         uint64_t max_attempts;
+        OrderedHashmap *options;
         usec_t request_sent;
         sd_event_source *timeout_t1;
         sd_event_source *timeout_t2;
@@ -100,6 +100,7 @@ struct sd_dhcp_client {
         void *userdata;
         sd_dhcp_lease *lease;
         usec_t start_delay;
+        int ip_service_type;
 };
 
 static const uint8_t default_req_opts[] = {
@@ -234,6 +235,7 @@ int sd_dhcp_client_set_mac(
 
         DHCP_CLIENT_DONT_DESTROY(client);
         bool need_restart = false;
+        int r;
 
         assert_return(client, -EINVAL);
         assert_return(addr, -EINVAL);
@@ -261,8 +263,11 @@ int sd_dhcp_client_set_mac(
         client->mac_addr_len = addr_len;
         client->arp_type = arp_type;
 
-        if (need_restart && client->state != DHCP_STATE_STOPPED)
-                sd_dhcp_client_start(client);
+        if (need_restart && client->state != DHCP_STATE_STOPPED) {
+                r = sd_dhcp_client_start(client);
+                if (r < 0)
+                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
+        }
 
         return 0;
 }
@@ -298,6 +303,7 @@ int sd_dhcp_client_set_client_id(
 
         DHCP_CLIENT_DONT_DESTROY(client);
         bool need_restart = false;
+        int r;
 
         assert_return(client, -EINVAL);
         assert_return(data, -EINVAL);
@@ -331,8 +337,11 @@ int sd_dhcp_client_set_client_id(
         memcpy(&client->client_id.raw.data, data, data_len);
         client->client_id_len = data_len + sizeof (client->client_id.type);
 
-        if (need_restart && client->state != DHCP_STATE_STOPPED)
-                sd_dhcp_client_start(client);
+        if (need_restart && client->state != DHCP_STATE_STOPPED) {
+                r = sd_dhcp_client_start(client);
+                if (r < 0)
+                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
+        }
 
         return 0;
 }
@@ -363,7 +372,7 @@ static int dhcp_client_set_iaid_duid_internal(
         if (duid) {
                 r = dhcp_validate_duid_len(duid_type, duid_len, true);
                 if (r < 0)
-                        return r;
+                        return log_dhcp_client_errno(client, r, "Failed to validate length of DUID: %m");
         }
 
         zero(client->client_id);
@@ -378,7 +387,7 @@ static int dhcp_client_set_iaid_duid_internal(
                                                      true,
                                                      &client->client_id.ns.iaid);
                         if (r < 0)
-                                return r;
+                                return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
                 }
         }
 
@@ -390,32 +399,32 @@ static int dhcp_client_set_iaid_duid_internal(
                 switch (duid_type) {
                 case DUID_TYPE_LLT:
                         if (client->mac_addr_len == 0)
-                                return -EOPNOTSUPP;
+                                return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
 
                         r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
                         break;
                 case DUID_TYPE_EN:
                         r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
                         break;
                 case DUID_TYPE_LL:
                         if (client->mac_addr_len == 0)
-                                return -EOPNOTSUPP;
+                                return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
 
                         r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
                         break;
                 case DUID_TYPE_UUID:
                         r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
                         break;
                 default:
-                        return -EINVAL;
+                        return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
                 }
 
         client->client_id_len = sizeof(client->client_id.type) + len +
@@ -424,7 +433,9 @@ static int dhcp_client_set_iaid_duid_internal(
         if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
                 log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
                 client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-                sd_dhcp_client_start(client);
+                r = sd_dhcp_client_start(client);
+                if (r < 0)
+                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
         }
 
         return 0;
@@ -534,6 +545,24 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt
         return 0;
 }
 
+int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&client->options, &dhcp_option_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(client->options, UINT_TO_PTR(v->option), v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp_option_ref(v);
+        return 0;
+}
+
 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
         assert_return(client, -EINVAL);
 
@@ -546,6 +575,14 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
         return 0;
 }
 
+int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
+        assert_return(client, -EINVAL);
+
+        client->ip_service_type = type;
+
+        return 0;
+}
+
 static int client_notify(sd_dhcp_client *client, int event) {
         assert(client);
 
@@ -778,7 +815,7 @@ static int dhcp_client_send_raw(
                 size_t len) {
 
         dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
-                                      INADDR_BROADCAST, DHCP_PORT_SERVER, len);
+                                      INADDR_BROADCAST, DHCP_PORT_SERVER, len, client->ip_service_type);
 
         return dhcp_network_send_raw_socket(client->fd, &client->link,
                                             packet, len);
@@ -787,6 +824,8 @@ static int dhcp_client_send_raw(
 static int client_send_discover(sd_dhcp_client *client) {
         _cleanup_free_ DHCPPacket *discover = NULL;
         size_t optoffset, optlen;
+        sd_dhcp_option *j;
+        Iterator i;
         int r;
 
         assert(client);
@@ -803,7 +842,9 @@ static int client_send_discover(sd_dhcp_client *client) {
            address be assigned, and may include the ’IP address lease time’
            option to suggest the lease time it would like.
          */
-        if (client->last_addr != INADDR_ANY) {
+        /* RFC7844 section 3:
+           SHOULD NOT contain any other option. */
+        if (!client->anonymize && client->last_addr != INADDR_ANY) {
                 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
                                        SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
                                        4, &client->last_addr);
@@ -848,6 +889,13 @@ static int client_send_discover(sd_dhcp_client *client) {
                         return r;
         }
 
+        ORDERED_HASHMAP_FOREACH(j, client->options, i) {
+                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                       j->option, j->length, j->data);
+                if (r < 0)
+                        return r;
+        }
+
         r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
         if (r < 0)
@@ -866,41 +914,6 @@ static int client_send_discover(sd_dhcp_client *client) {
         return 0;
 }
 
-static int client_send_release(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *release = NULL;
-        size_t optoffset, optlen;
-        int r;
-
-        assert(client);
-        assert(!IN_SET(client->state, DHCP_STATE_STOPPED));
-
-        r = client_message_init(client, &release, DHCP_RELEASE,
-                                &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        /* Fill up release IP and MAC */
-        release->dhcp.ciaddr = client->lease->address;
-        memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
-
-        r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
-                               SD_DHCP_OPTION_END, 0, NULL);
-        if (r < 0)
-                return r;
-
-        r = dhcp_network_send_udp_socket(client->fd,
-                                         client->lease->server_address,
-                                         DHCP_PORT_SERVER,
-                                         &release->dhcp,
-                                         sizeof(DHCPMessage) + optoffset);
-        if (r < 0)
-                return r;
-
-        log_dhcp_client(client, "RELEASE");
-
-        return 0;
-}
-
 static int client_send_request(sd_dhcp_client *client) {
         _cleanup_free_ DHCPPacket *request = NULL;
         size_t optoffset, optlen;
@@ -1000,15 +1013,14 @@ static int client_send_request(sd_dhcp_client *client) {
         if (r < 0)
                 return r;
 
-        if (client->state == DHCP_STATE_RENEWING) {
+        if (client->state == DHCP_STATE_RENEWING)
                 r = dhcp_network_send_udp_socket(client->fd,
                                                  client->lease->server_address,
                                                  DHCP_PORT_SERVER,
                                                  &request->dhcp,
                                                  sizeof(DHCPMessage) + optoffset);
-        } else {
+        else
                 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
-        }
         if (r < 0)
                 return r;
 
@@ -1216,7 +1228,7 @@ static int client_initialize_time_events(sd_dhcp_client *client) {
         assert(client);
         assert(client->event);
 
-        if (client->start_delay) {
+        if (client->start_delay > 0) {
                 assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
                 usec += client->start_delay;
         }
@@ -1667,7 +1679,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
                                 goto error;
                         }
 
-                        r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port);
+                        r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
                         if (r < 0) {
                                 log_dhcp_client(client, "could not bind UDP socket");
                                 goto error;
@@ -1887,6 +1899,17 @@ static int client_receive_message_raw(
         return client_handle_message(client, &packet->dhcp, len);
 }
 
+int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
+        assert_return(client, -EINVAL);
+        assert_return(client->fd >= 0, -EINVAL);
+
+        client->start_delay = 0;
+        client->attempt = 1;
+        client->state = DHCP_STATE_RENEWING;
+
+        return client_initialize_time_events(client);
+}
+
 int sd_dhcp_client_start(sd_dhcp_client *client) {
         int r;
 
@@ -1915,8 +1938,35 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
 
 int sd_dhcp_client_send_release(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
+        assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
+        assert_return(client->lease, -EUNATCH);
+
+        _cleanup_free_ DHCPPacket *release = NULL;
+        size_t optoffset, optlen;
+        int r;
+
+        r = client_message_init(client, &release, DHCP_RELEASE, &optlen, &optoffset);
+        if (r < 0)
+                return r;
+
+        /* Fill up release IP and MAC */
+        release->dhcp.ciaddr = client->lease->address;
+        memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
+
+        r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
+                               SD_DHCP_OPTION_END, 0, NULL);
+        if (r < 0)
+                return r;
 
-        client_send_release(client);
+        r = dhcp_network_send_udp_socket(client->fd,
+                                         client->lease->server_address,
+                                         DHCP_PORT_SERVER,
+                                         &release->dhcp,
+                                         sizeof(DHCPMessage) + optoffset);
+        if (r < 0)
+                return r;
+
+        log_dhcp_client(client, "RELEASE");
 
         return 0;
 }
@@ -1966,7 +2016,8 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
 }
 
 static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
-        assert(client);
+        if (!client)
+                return NULL;
 
         log_dhcp_client(client, "FREE");
 
@@ -1985,17 +2036,16 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
         free(client->hostname);
         free(client->vendor_class_identifier);
         client->user_class = strv_free(client->user_class);
+        ordered_hashmap_free(client->options);
         return mfree(client);
 }
 
 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free);
 
 int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
-        _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
-
         assert_return(ret, -EINVAL);
 
-        client = new(sd_dhcp_client, 1);
+        _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = new(sd_dhcp_client, 1);
         if (!client)
                 return -ENOMEM;
 
@@ -2008,6 +2058,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
                 .port = DHCP_PORT_CLIENT,
                 .anonymize = !!anonymize,
                 .max_attempts = (uint64_t) -1,
+                .ip_service_type = -1,
         };
         /* NOTE: this could be moved to a function. */
         if (anonymize) {
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
index 7559d066..ac6fe3f4 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -7,9 +7,7 @@
 
 #include <arpa/inet.h>
 #include <errno.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -122,6 +120,17 @@ int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
         return (int) lease->ntp_size;
 }
 
+int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        if (lease->sip_size <= 0)
+                return -ENODATA;
+
+        *addr = lease->sip;
+        return (int) lease->sip_size;
+}
+
 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
         assert_return(lease, -EINVAL);
         assert_return(domainname, -EINVAL);
@@ -271,6 +280,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
         free(lease->domainname);
         free(lease->dns);
         free(lease->ntp);
+        free(lease->sip);
         free(lease->static_route);
         free(lease->client_id);
         free(lease->vendor_specific);
@@ -404,6 +414,36 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add
         return 0;
 }
 
+static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
+        assert(option);
+        assert(ret);
+        assert(n_ret);
+
+        if (len <= 0) {
+                *ret = mfree(*ret);
+                *n_ret = 0;
+        } else {
+                size_t n_addresses;
+                struct in_addr *addresses;
+                int l = len - 1;
+
+                if (l % 4 != 0)
+                        return -EINVAL;
+
+                n_addresses = l / 4;
+
+                addresses = newdup(struct in_addr, option + 1, n_addresses);
+                if (!addresses)
+                        return -ENOMEM;
+
+                free(*ret);
+                *ret = addresses;
+                *n_ret = n_addresses;
+        }
+
+        return 0;
+}
+
 static int lease_parse_routes(
                 const uint8_t *option, size_t len,
                 struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
@@ -557,6 +597,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                         log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
                 break;
 
+        case SD_DHCP_OPTION_SIP_SERVER:
+                r = lease_parse_sip_server(option, len, &lease->sip, &lease->sip_size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse SIP server, ignoring: %m");
+                break;
+
         case SD_DHCP_OPTION_STATIC_ROUTE:
                 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
                 if (r < 0)
@@ -895,6 +941,13 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
                 fputc('\n', f);
         }
 
+        r = sd_dhcp_lease_get_sip(lease, &addresses);
+        if (r > 0) {
+                fputs("SIP=", f);
+                serialize_in_addrs(f, addresses, r, false, NULL);
+                fputc('\n', f);
+        }
+
         r = sd_dhcp_lease_get_domainname(lease, &string);
         if (r >= 0)
                 fprintf(f, "DOMAINNAME=%s\n", string);
@@ -985,6 +1038,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                 *broadcast = NULL,
                 *dns = NULL,
                 *ntp = NULL,
+                *sip = NULL,
                 *mtu = NULL,
                 *routes = NULL,
                 *domains = NULL,
@@ -1013,6 +1067,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                            "BROADCAST", &broadcast,
                            "DNS", &dns,
                            "NTP", &ntp,
+                           "SIP", &sip,
                            "MTU", &mtu,
                            "DOMAINNAME", &lease->domainname,
                            "HOSTNAME", &lease->hostname,
@@ -1117,6 +1172,14 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                         lease->ntp_size = r;
         }
 
+        if (sip) {
+                r = deserialize_in_addrs(&lease->sip, sip);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip);
+                else
+                        lease->ntp_size = r;
+        }
+
         if (mtu) {
                 r = safe_atou16(mtu, &lease->mtu);
                 if (r < 0)
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index f67a45bd..e1150f98 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -6,7 +6,6 @@
 #include "nm-sd-adapt-core.h"
 
 #include <errno.h>
-#include <string.h>
 #include <sys/ioctl.h>
 #include <linux/if_arp.h>
 #include <linux/if_infiniband.h>
@@ -31,6 +30,9 @@
 
 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
 
+#define IRT_DEFAULT (1 * USEC_PER_DAY)
+#define IRT_MINIMUM (600 * USEC_PER_SEC)
+
 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
 enum {
         DHCP6_REQUEST_IA_NA                     = 1,
@@ -45,6 +47,7 @@ struct sd_dhcp6_client {
         sd_event *event;
         int event_priority;
         int ifindex;
+        DHCP6Address hint_pd_prefix;
         struct in6_addr local_address;
         uint8_t mac_addr[MAX_MAC_ADDR_LEN];
         size_t mac_addr_len;
@@ -73,6 +76,8 @@ struct sd_dhcp6_client {
         void *userdata;
         struct duid duid;
         size_t duid_len;
+        usec_t information_request_time_usec;
+        usec_t information_refresh_time_usec;
 };
 
 static const uint16_t default_req_opts[] = {
@@ -184,6 +189,22 @@ int sd_dhcp6_client_set_mac(
         return 0;
 }
 
+int sd_dhcp6_client_set_prefix_delegation_hint(
+                sd_dhcp6_client *client,
+                uint8_t prefixlen,
+                const struct in6_addr *pd_address) {
+
+        assert_return(client, -EINVAL);
+        assert_return(pd_address, -EINVAL);
+
+        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+        client->hint_pd_prefix.iapdprefix.address = *pd_address;
+        client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
+
+        return 0;
+}
+
 static int client_ensure_duid(sd_dhcp6_client *client) {
         if (client->duid_len != 0)
                 return 0;
@@ -213,7 +234,7 @@ static int dhcp6_client_set_duid_internal(
                 if (r < 0) {
                         r = dhcp_validate_duid_len(duid_type, duid_len, false);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
                         log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
                 }
 
@@ -225,32 +246,32 @@ static int dhcp6_client_set_duid_internal(
                 switch (duid_type) {
                 case DUID_TYPE_LLT:
                         if (client->mac_addr_len == 0)
-                                return -EOPNOTSUPP;
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
 
                         r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
                         break;
                 case DUID_TYPE_EN:
                         r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
                         break;
                 case DUID_TYPE_LL:
                         if (client->mac_addr_len == 0)
-                                return -EOPNOTSUPP;
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
 
                         r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
                         break;
                 case DUID_TYPE_UUID:
                         r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
                         break;
                 default:
-                        return -EINVAL;
+                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
                 }
 #else /* NM_IGNORED */
                 g_return_val_if_reached (-EINVAL);
@@ -493,7 +514,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 }
 
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
+                        r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
                         if (r < 0)
                                 return r;
 
@@ -531,7 +552,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 }
 
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
                                 return r;
 
@@ -557,7 +578,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 }
 
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
                                 return r;
 
@@ -826,6 +847,7 @@ static int client_parse_message(
         uint32_t lt_t1 = ~0, lt_t2 = ~0;
         bool clientid = false;
         size_t pos = 0;
+        usec_t irt = IRT_DEFAULT;
         int r;
 
         assert(client);
@@ -1000,6 +1022,13 @@ static int client_parse_message(
                                 return r;
 
                         break;
+
+                case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
+                        if (optlen != 4)
+                                return -EINVAL;
+
+                        irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
+                        break;
                 }
 
                 pos += offsetof(DHCP6Option, data) + optlen;
@@ -1031,6 +1060,8 @@ static int client_parse_message(
                 }
         }
 
+        client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
+
         return 0;
 }
 
@@ -1431,8 +1462,15 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
                 client->fd = r;
         }
 
-        if (client->information_request)
+        if (client->information_request) {
+                usec_t t = now(CLOCK_MONOTONIC);
+
+                if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
+                        return 0;
+
+                client->information_request_time_usec = t;
                 state = DHCP6_STATE_INFORMATION_REQUEST;
+        }
 
         log_dhcp6_client(client, "Started in %s mode",
                          client->information_request? "Information request":
@@ -1521,6 +1559,8 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
                 .request = DHCP6_REQUEST_IA_NA,
                 .fd = -1,
                 .req_opts_len = ELEMENTSOF(default_req_opts),
+                .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
+                .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1,
                 .req_opts = TAKE_PTR(req_opts),
         };
 
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
index e7561480..3efa8170 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
@@ -10,7 +10,6 @@
 #include <netinet/if_ether.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "sd-ipv4acd.h"
 
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index daac8839..2d17b4fe 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -9,7 +9,6 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "sd-id128.h"
 #include "sd-ipv4acd.h"
diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c
index c3781706..7c4566e3 100644
--- a/src/systemd/src/libsystemd/sd-event/sd-event.c
+++ b/src/systemd/src/libsystemd/sd-event/sd-event.c
@@ -18,7 +18,7 @@
 #include "list.h"
 #include "macro.h"
 #include "memory-util.h"
-#include "missing.h"
+#include "missing_syscall.h"
 #include "prioq.h"
 #include "process-util.h"
 #include "set.h"
@@ -773,11 +773,13 @@ static void source_disconnect(sd_event_source *s) {
 
         event = s->event;
 
-        s->type = _SOURCE_EVENT_SOURCE_TYPE_INVALID;
         s->event = NULL;
         LIST_REMOVE(sources, event->sources, s);
         event->n_sources--;
 
+        /* Note that we don't invalidate the type here, since we still need it in order to close the fd or
+         * pidfd associated with this event source, which we'll do only on source_free(). */
+
         if (!s->floating)
                 sd_event_unref(event);
 }
@@ -2556,7 +2558,7 @@ static int process_child(sd_event *e) {
                                  * benefit in leaving it queued */
 
                                 assert(s->child.options & (WSTOPPED|WCONTINUED));
-                                waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
+                                (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
                         }
 
                         r = source_set_pending(s, true);
diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
index eeb29e97..c0d0fe81 100644
--- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
@@ -15,7 +15,7 @@
 #include "io-util.h"
 #include "khash.h"
 #include "macro.h"
-#include "missing.h"
+#include "missing_syscall.h"
 #include "random-util.h"
 #include "user-util.h"
 #include "util.h"
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
index ab62368e..f97e35b6 100644
--- a/src/systemd/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -26,6 +26,7 @@
 #include <stdbool.h>
 
 #include "sd-dhcp-lease.h"
+#include "sd-dhcp-option.h"
 #include "sd-event.h"
 
 #include "_sd-common.h"
@@ -87,6 +88,7 @@ enum {
         SD_DHCP_OPTION_NEW_POSIX_TIMEZONE          = 100,
         SD_DHCP_OPTION_NEW_TZDB_TIMEZONE           = 101,
         SD_DHCP_OPTION_DOMAIN_SEARCH_LIST          = 119,
+        SD_DHCP_OPTION_SIP_SERVER                  = 120,
         SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
         SD_DHCP_OPTION_PRIVATE_BASE                = 224,
        /* Windows 10 option to send when Anonymize=true */
@@ -173,10 +175,16 @@ int sd_dhcp_client_set_user_class(
 int sd_dhcp_client_get_lease(
                 sd_dhcp_client *client,
                 sd_dhcp_lease **ret);
+int sd_dhcp_client_set_service_type(
+                sd_dhcp_client *client,
+                int type);
+
+int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v);
 
 int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
 int sd_dhcp_client_send_release(sd_dhcp_client *client);
+int sd_dhcp_client_send_renew(sd_dhcp_client *client);
 
 sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);
 sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h
index d299c791..b80d607f 100644
--- a/src/systemd/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp-lease.h
@@ -44,6 +44,7 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
 int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
diff --git a/src/systemd/src/systemd/sd-dhcp-option.h b/src/systemd/src/systemd/sd-dhcp-option.h
new file mode 100644
index 00000000..45dbd279
--- /dev/null
+++ b/src/systemd/src/systemd/sd-dhcp-option.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#ifndef foosddhcpoptionhfoo
+#define foosddhcpoptionhfoo
+
+/***
+  Copyright © 2013 Intel Corporation. All rights reserved.
+  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 <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp_option sd_dhcp_option;
+
+int sd_dhcp_option_new(uint8_t option, const void *data, size_t length, sd_dhcp_option **ret);
+sd_dhcp_option *sd_dhcp_option_ref(sd_dhcp_option *ra);
+sd_dhcp_option *sd_dhcp_option_unref(sd_dhcp_option *ra);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_option, sd_dhcp_option_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index 43d38f5c..be34d43e 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -66,6 +66,7 @@ enum {
         SD_DHCP6_OPTION_IA_PD_PREFIX               = 26,  /* RFC 3633, prefix delegation */
 
         SD_DHCP6_OPTION_SNTP_SERVERS               = 31,  /* RFC 4075, deprecated */
+        SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME   = 32,  /* RFC 8415, sec. 21.23 */
 
         /* option code 35 is unassigned */
 
@@ -119,6 +120,10 @@ int sd_dhcp6_client_get_information_request(
 int sd_dhcp6_client_set_request_option(
                 sd_dhcp6_client *client,
                 uint16_t option);
+int sd_dhcp6_client_set_prefix_delegation_hint(
+                sd_dhcp6_client *client,
+                uint8_t prefixlen,
+                const struct in6_addr *pd_address);
 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
                                           int *delegation);
 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
diff --git a/src/tests/config/meson.build b/src/tests/config/meson.build
index f65f90bb..e3ebfa19 100644
--- a/src/tests/config/meson.build
+++ b/src/tests/config/meson.build
@@ -1,3 +1,5 @@
+test_config_dir = meson.current_source_dir()
+
 test_unit = 'test-config'
 
 sources = files(
@@ -5,12 +7,11 @@ sources = files(
   'test-config.c',
 )
 
-test_config_dir = meson.current_source_dir()
-
 exe = executable(
   test_unit,
   sources,
-  dependencies: test_nm_dep,
+  dependencies: libnetwork_manager_test_dep,
+  c_args: test_c_flags,
 )
 
 test(
diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c
index dd624b44..6e59a4aa 100644
--- a/src/tests/config/nm-test-device.c
+++ b/src/tests/config/nm-test-device.c
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/src/tests/config/nm-test-device.h b/src/tests/config/nm-test-device.h
index 586bbe6d..f10c1589 100644
--- a/src/tests/config/nm-test-device.h
+++ b/src/tests/config/nm-test-device.h
@@ -1,20 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_TEST_DEVICE_H__
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index a5ef1431..1c28e976 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2013 Red Hat, Inc.
- *
+ * Copyright (C) 2013 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -537,7 +523,7 @@ test_config_confdir (void)
 	g_assert_cmpstr (value, ==, "VAL5");
 	g_free (value);
 
-	nm_config_data_log (nm_config_get_data_orig (config), ">>> TEST: ", " ", NULL);
+	nm_config_data_log (nm_config_get_data_orig (config), ">>> TEST: ", " ", "/test/file/name", NULL);
 }
 
 static void
diff --git a/src/tests/meson.build b/src/tests/meson.build
index b2bc13ab..ac877e18 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -14,7 +14,8 @@ foreach test_unit: test_units
   exe = executable(
     test_unit,
     test_unit + '.c',
-    dependencies: test_nm_dep,
+    dependencies: libnetwork_manager_test_dep,
+    c_args: test_c_flags,
   )
 
   test(
@@ -27,21 +28,22 @@ endforeach
 
 test_unit = 'test-systemd'
 
-cflags = [
+c_flags = [
   '-DNETWORKMANAGER_COMPILATION_TEST',
   '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD',
 ]
 
+links = [
+  libnm_systemd_core,
+  libnm_systemd_shared,
+]
+
 exe = executable(
   test_unit,
   test_unit + '.c',
-  include_directories: src_inc,
-  dependencies: libnm_core_dep,
-  c_args: cflags,
-  link_with: [
-    libnm_systemd_core,
-    libnm_systemd_shared,
-  ],
+  dependencies: daemon_nm_default_dep,
+  c_args: c_flags,
+  link_with: links,
 )
 
 test(
diff --git a/src/tests/test-core-with-expect.c b/src/tests/test-core-with-expect.c
index 2d111a41..74e1043b 100644
--- a/src/tests/test-core-with-expect.c
+++ b/src/tests/test-core-with-expect.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2014 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -58,10 +44,13 @@ test_nm_utils_monotonic_timestamp_as_boottime (void)
 		g_assert_cmpint (now_boottime_2, >=, now_boottime);
 		g_assert_cmpint (now_boottime_2 - now_boottime, <=, NM_UTILS_NS_PER_SECOND / 10);
 
+		g_assert_cmpint (now, ==, nm_utils_monotonic_timestamp_from_boottime (now_boottime_2, 1));
+
 		for (timestamp_ns_per_tick = 1; timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND; timestamp_ns_per_tick *= 10) {
 			now_boottime_3 = nm_utils_monotonic_timestamp_as_boottime (now / timestamp_ns_per_tick, timestamp_ns_per_tick);
 
 			g_assert_cmpint (now_boottime_2 / timestamp_ns_per_tick, ==, now_boottime_3);
+			g_assert_cmpint (now / timestamp_ns_per_tick, ==, nm_utils_monotonic_timestamp_from_boottime (now_boottime_3, timestamp_ns_per_tick));
 		}
 	}
 }
diff --git a/src/tests/test-core.c b/src/tests/test-core.c
index 3eaad104..f55f3dff 100644
--- a/src/tests/test-core.c
+++ b/src/tests/test-core.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2014 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -2098,10 +2084,12 @@ test_nm_utils_dhcp_client_id_systemd_node_specific (gconstpointer test_data)
 		gs_unref_bytes GBytes *client_id = NULL;
 		const guint8 *cid;
 		guint32 iaid = d->iaid_ifname;
+		guint32 tmp;
 
-		client_id = nm_utils_dhcp_client_id_systemd_node_specific_full (legacy_unstable_byteorder,
-		                                                                (const guint8 *) d->ifname,
-		                                                                strlen (d->ifname),
+		tmp = nm_utils_create_dhcp_iaid (legacy_unstable_byteorder,
+		                                 (const guint8 *) d->ifname,
+		                                 strlen (d->ifname));
+		client_id = nm_utils_dhcp_client_id_systemd_node_specific_full (tmp,
 		                                                                (const guint8 *) &d->machine_id,
 		                                                                sizeof (d->machine_id));
 
diff --git a/src/tests/test-dcb.c b/src/tests/test-dcb.c
index 55206f1d..82d19cee 100644
--- a/src/tests/test-dcb.c
+++ b/src/tests/test-dcb.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2013 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c
index 5afd8670..cd7dc8d8 100644
--- a/src/tests/test-ip4-config.c
+++ b/src/tests/test-ip4-config.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2013 - 2014 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c
index bc212ba7..401d1eee 100644
--- a/src/tests/test-ip6-config.c
+++ b/src/tests/test-ip6-config.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2013 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c
index 090dd691..faed0e56 100644
--- a/src/tests/test-systemd.c
+++ b/src/tests/test-systemd.c
@@ -1,18 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c
index f3eb66ef..0c94e2d0 100644
--- a/src/tests/test-utils.c
+++ b/src/tests/test-utils.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2015 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/tests/test-wired-defname.c b/src/tests/test-wired-defname.c
index 27e0590b..4b557d0c 100644
--- a/src/tests/test-wired-defname.c
+++ b/src/tests/test-wired-defname.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c
index 598af04e..6a25ddda 100644
--- a/src/vpn/nm-vpn-connection.c
+++ b/src/vpn/nm-vpn-connection.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2013 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -111,8 +97,7 @@ typedef struct {
 	NMVpnPluginInfo *plugin_info;
 	char *bus_name;
 
-	/* Firewall */
-	NMFirewallManagerCallId fw_call;
+	NMFirewallManagerCallId *fw_call;
 
 	NMNetns *netns;
 
@@ -1195,7 +1180,7 @@ _cleanup_failed_config (NMVpnConnection *self)
 
 static void
 fw_change_zone_cb (NMFirewallManager *firewall_manager,
-                   NMFirewallManagerCallId call_id,
+                   NMFirewallManagerCallId *call_id,
                    GError *error,
                    gpointer user_data)
 {
diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h
index 1527b689..43242a8e 100644
--- a/src/vpn/nm-vpn-connection.h
+++ b/src/vpn/nm-vpn-connection.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2011 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
diff --git a/src/vpn/nm-vpn-manager.c b/src/vpn/nm-vpn-manager.c
index 36043c93..4c94a260 100644
--- a/src/vpn/nm-vpn-manager.c
+++ b/src/vpn/nm-vpn-manager.c
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2012 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
diff --git a/src/vpn/nm-vpn-manager.h b/src/vpn/nm-vpn-manager.h
index bcbd19f1..6a78acd2 100644
--- a/src/vpn/nm-vpn-manager.h
+++ b/src/vpn/nm-vpn-manager.h
@@ -1,19 +1,5 @@
-/* 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.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2011 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */