about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c21
-rw-r--r--src/devices/nm-device-ethernet-utils.c2
-rw-r--r--src/devices/nm-device.c150
-rw-r--r--src/devices/nm-device.h29
-rw-r--r--src/devices/wifi/nm-device-iwd.c304
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c4
-rw-r--r--src/devices/wifi/nm-device-wifi.c15
-rw-r--r--src/devices/wifi/nm-iwd-manager.c26
-rw-r--r--src/devices/wifi/nm-iwd-manager.h3
-rw-r--r--src/devices/wwan/nm-modem-broadband.c2
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.c1
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c19
-rw-r--r--src/dhcp/nm-dhcp-helper.c2
-rw-r--r--src/dhcp/nm-dhcp-manager.c2
-rw-r--r--src/dhcp/nm-dhcp-systemd.c39
-rw-r--r--src/dhcp/tests/test-dhcp-dhclient.c22
-rw-r--r--src/dns/nm-dns-dnsmasq.c26
-rw-r--r--src/dns/nm-dns-systemd-resolved.c30
-rw-r--r--src/initrd/nm-initrd-generator.c139
-rw-r--r--src/initrd/nm-initrd-generator.h45
-rw-r--r--src/initrd/nmi-cmdline-reader.c714
-rw-r--r--src/initrd/nmi-ibft-reader.c441
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name1
-rw-r--r--src/initrd/tests/sysfs-dhcp/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs-dhcp/class/net/eth1/address1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port1
-rw-r--r--src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name1
-rw-r--r--src/initrd/tests/sysfs-static/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs-static/class/net/eth1/address1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target0/target-name1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target1/index1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target1/lun1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target1/port1
-rw-r--r--src/initrd/tests/sysfs-static/firmware/ibft/target1/target-name1
-rw-r--r--src/initrd/tests/sysfs-vlan/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name1
-rw-r--r--src/initrd/tests/sysfs/class/net/eth0/address1
-rw-r--r--src/initrd/tests/sysfs/class/net/eth2/address1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/acpi_header/signature1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/flags1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/index1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/mac1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/origin1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/flags1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/index1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/mac1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/origin1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/initiator/flags1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/initiator/index1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/initiator/isns-server1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/initiator/slp-server1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target0/chap-type1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target0/flags1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target0/index1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target0/ip-addr1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target0/lun1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target0/port1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/chap-type1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/flags1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/index1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/ip-addr1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/lun1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/port1
-rw-r--r--src/initrd/tests/sysfs/firmware/ibft/target2/target-name1
-rw-r--r--src/initrd/tests/test-cmdline-reader.c787
-rw-r--r--src/initrd/tests/test-ibft-reader.c286
-rw-r--r--src/main.c9
-rw-r--r--src/meson.build28
-rw-r--r--src/ndisc/nm-fake-ndisc.c2
-rw-r--r--src/ndisc/nm-lndp-ndisc.c16
-rw-r--r--src/ndisc/nm-ndisc-private.h2
-rw-r--r--src/ndisc/nm-ndisc.c352
-rw-r--r--src/ndisc/nm-ndisc.h3
-rw-r--r--src/ndisc/tests/test-ndisc-fake.c7
-rw-r--r--src/nm-config.c184
-rw-r--r--src/nm-config.h12
-rw-r--r--src/nm-connectivity.c236
-rw-r--r--src/nm-connectivity.h7
-rw-r--r--src/nm-dbus-manager.c32
-rw-r--r--src/nm-iface-helper.c4
-rw-r--r--src/nm-ip4-config.c132
-rw-r--r--src/nm-ip4-config.h13
-rw-r--r--src/nm-ip6-config.c60
-rw-r--r--src/nm-ip6-config.h3
-rw-r--r--src/nm-manager.c153
-rw-r--r--src/platform/nm-netlink.h2
-rw-r--r--src/settings/nm-settings-connection.c2
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-bad-dns121
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-bad-dns221
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-bad-entry20
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway21
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr21
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-bad-record18
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-dhcp33
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-static35
-rwxr-xr-xsrc/settings/plugins/ibft/tests/iscsiadm-test-vlan19
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build9
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c2
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c20
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c4
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c8
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.h3
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c141
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c85
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.h6
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c100
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h16
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c32
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.h1
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c403
-rw-r--r--src/supplicant/nm-supplicant-config.c6
-rw-r--r--src/supplicant/nm-supplicant-settings-verify.c1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c13
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c2
265 files changed, 4285 insertions, 1338 deletions
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index b69b4320..b722f692 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -227,21 +227,12 @@ pan_connection_check_create (NMBluezDevice *self)
 	              NULL);
 	nm_connection_add_setting (connection, setting);
 
-	/* Setting: IPv4 */
-	setting = nm_setting_ip4_config_new ();
-	g_object_set (G_OBJECT (setting),
-	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
-	              NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
-	              NULL);
-	nm_connection_add_setting (connection, setting);
-
-	/* Setting: IPv6 */
-	setting = nm_setting_ip6_config_new ();
-	g_object_set (G_OBJECT (setting),
-	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
-	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
-	              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,
diff --git a/src/devices/nm-device-ethernet-utils.c b/src/devices/nm-device-ethernet-utils.c
index 8bdb1991..b18eadfa 100644
--- a/src/devices/nm-device-ethernet-utils.c
+++ b/src/devices/nm-device-ethernet-utils.c
@@ -29,7 +29,7 @@ nm_device_ethernet_utils_get_default_wired_name (GHashTable *existing_ids)
 	int i;
 
 	/* Find the next available unique connection name */
-	for (i = 1; i <= G_MAXINT; i++) {
+	for (i = 1; i < G_MAXINT; i++) {
 		temp = g_strdup_printf (_("Wired connection %d"), i);
 		if (   !existing_ids
 		    || !g_hash_table_contains (existing_ids, temp))
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 8ca899a2..26ea06bd 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -464,6 +464,7 @@ typedef struct _NMDevicePrivate {
 		gulong          state_sigid;
 		NMDhcp4Config * config;
 		char *          pac_url;
+		char *          root_path;
 		bool            was_active;
 		guint           grace_id;
 	} dhcp4;
@@ -2813,7 +2814,6 @@ static void
 concheck_cb (NMConnectivity *connectivity,
              NMConnectivityCheckHandle *c_handle,
              NMConnectivityState state,
-             GError *error,
              gpointer user_data)
 {
 	_nm_unused gs_unref_object NMDevice *self_keep_alive = NULL;
@@ -2834,7 +2834,7 @@ concheck_cb (NMConnectivity *connectivity,
 	handle->c_handle = NULL;
 	self = handle->self;
 
-	if (nm_utils_error_is_cancelled (error, FALSE)) {
+	if (state == NM_CONNECTIVITY_CANCELLED) {
 		/* the only place where we nm_connectivity_check_cancel(@c_handle), is
 		 * from inside concheck_handle_complete(). This is a recursive call,
 		 * nothing to do. */
@@ -2843,15 +2843,14 @@ concheck_cb (NMConnectivity *connectivity,
 		return;
 	}
 
+	/* we keep NMConnectivity instance alive. It cannot be disposing. */
+	nm_assert (state != NM_CONNECTIVITY_DISPOSING);
+
 	self_keep_alive = g_object_ref (self);
 
-	_LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, state:%s%s%s%s)",
+	_LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, state:%s)",
 	       (long long unsigned) handle->seq,
-	       nm_connectivity_state_to_string (state),
-	       NM_PRINT_FMT_QUOTED (error, ", error: ", error->message, "", ""));
-
-	/* we keep NMConnectivity instance alive. It cannot be disposing. */
-	nm_assert (!nm_utils_error_is_cancelled (error, TRUE));
+	       nm_connectivity_state_to_string (state));
 
 	/* keep @self alive, while we invoke callbacks. */
 	priv = NM_DEVICE_GET_PRIVATE (self);
@@ -3506,7 +3505,8 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
 		guint32 lifetime, preferred;
 		gint32 base;
 
-		if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
+		if (   IN6_IS_ADDR_UNSPECIFIED (&addr->address)
+		    || IN6_IS_ADDR_LINKLOCAL (&addr->address))
 			continue;
 
 		if (   addr->n_ifa_flags & IFA_F_TENTATIVE
@@ -3580,6 +3580,7 @@ device_link_changed (NMDevice *self)
 	gboolean ip_ifname_changed = FALSE;
 	nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL;
 	const NMPlatformLink *pllink;
+	const char *str;
 	int ifindex;
 	gboolean was_up;
 	gboolean update_unmanaged_specs = FALSE;
@@ -3594,7 +3595,23 @@ device_link_changed (NMDevice *self)
 
 	pllink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (pllink));
 
-	nm_device_update_from_platform_link (self, pllink);
+	str = nm_platform_link_get_udi (nm_device_get_platform (self), pllink->ifindex);
+	if (!nm_streq0 (str, priv->udi)) {
+		g_free (priv->udi);
+		priv->udi = g_strdup (str);
+		_notify (self, PROP_UDI);
+	}
+
+	if (!nm_streq0 (pllink->driver, priv->driver)) {
+		g_free (priv->driver);
+		priv->driver = g_strdup (pllink->driver);
+		_notify (self, PROP_DRIVER);
+	}
+
+	_set_mtu (self, pllink->mtu);
+
+	if (ifindex == nm_device_get_ip_ifindex (self))
+		_stats_update_counters_from_pllink (self, pllink);
 
 	had_hw_addr = (priv->hw_addr != NULL);
 	nm_device_update_hw_address (self);
@@ -4582,9 +4599,23 @@ nm_device_owns_iface (NMDevice *self, const char *iface)
 NMConnection *
 nm_device_new_default_connection (NMDevice *self)
 {
-	if (NM_DEVICE_GET_CLASS (self)->new_default_connection)
-		return NM_DEVICE_GET_CLASS (self)->new_default_connection (self);
-	return NULL;
+	NMConnection *connection;
+	GError *error = NULL;
+
+	if (!NM_DEVICE_GET_CLASS (self)->new_default_connection)
+		return NULL;
+
+	connection = NM_DEVICE_GET_CLASS (self)->new_default_connection (self);
+	if (!connection)
+		return NULL;
+
+	if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
+		_LOGD (LOGD_DEVICE, "device generated an invalid default connection: %s", error->message);
+		g_error_free (error);
+		g_return_val_if_reached (NULL);
+	}
+
+	return connection;
 }
 
 static void
@@ -5043,11 +5074,15 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config)
 		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 	}
 
-	if (!unconfigure_ip_config)
-		return;
-
-	nm_device_set_ip_config (self, AF_INET, NULL, FALSE, NULL);
-	nm_device_set_ip_config (self, AF_INET6, NULL, FALSE, NULL);
+	if (unconfigure_ip_config) {
+		nm_device_set_ip_config (self, AF_INET, NULL, FALSE, NULL);
+		nm_device_set_ip_config (self, AF_INET6, NULL, FALSE, NULL);
+	} else {
+		if (priv->dhcp4.client)
+			nm_dhcp_client_stop (priv->dhcp4.client, FALSE);
+		if (priv->dhcp6.client)
+			nm_dhcp_client_stop (priv->dhcp6.client, FALSE);
+	}
 }
 
 static gboolean
@@ -5451,7 +5486,7 @@ nm_device_generate_connection (NMDevice *self,
 
 	klass->update_connection (self, connection);
 
-	if (!nm_connection_verify (connection, &local)) {
+	if (!nm_connection_normalize (connection, NULL, NULL, error)) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 		             "generated connection does not verify: %s",
 		             local->message);
@@ -6955,6 +6990,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 
 	nm_clear_g_source (&priv->dhcp4.grace_id);
 	g_clear_pointer (&priv->dhcp4.pac_url, g_free);
+	g_clear_pointer (&priv->dhcp4.root_path, g_free);
 
 	if (priv->dhcp4.client) {
 		/* Stop any ongoing DHCP transaction on this device */
@@ -7328,6 +7364,9 @@ dhcp4_state_changed (NMDhcpClient *client,
 		priv->dhcp4.pac_url = g_strdup (g_hash_table_lookup (options, "wpad"));
 		nm_device_set_proxy_config (self, priv->dhcp4.pac_url);
 
+		g_free (priv->dhcp4.root_path);
+		priv->dhcp4.root_path = g_strdup (g_hash_table_lookup (options, "root_path"));
+
 		nm_dhcp4_config_set_options (priv->dhcp4.config, options);
 		_notify (self, PROP_DHCP4_CONFIG);
 
@@ -10780,8 +10819,10 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 					nm_ip4_config_update_routes_metric ((NMIP4Config *) priv->wwan_ip_config_4.orig,
 					                                    nm_device_get_route_metric (self, AF_INET));
 				}
-				if (priv->dhcp4.client)
-					nm_dhcp_client_set_route_metric (priv->dhcp4.client, metric_new);
+				if (priv->dhcp4.client) {
+					nm_dhcp_client_set_route_metric (priv->dhcp4.client,
+					                                 nm_device_get_route_metric (self, AF_INET));
+				}
 			}
 		}
 
@@ -10851,8 +10892,10 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 					nm_ip6_config_update_routes_metric ((NMIP6Config *) priv->wwan_ip_config_6.orig,
 					                                    nm_device_get_route_metric (self, AF_INET6));
 				}
-				if (priv->dhcp6.client)
-					nm_dhcp_client_set_route_metric (priv->dhcp6.client, metric_new);
+				if (priv->dhcp6.client) {
+					nm_dhcp_client_set_route_metric (priv->dhcp6.client,
+					                                 nm_device_get_route_metric (self, AF_INET6));
+				}
 			}
 		}
 
@@ -12521,7 +12564,9 @@ nm_device_get_firmware_missing (NMDevice *self)
 }
 
 static void
-intersect_ext_config (NMDevice *self, AppliedConfig *config)
+intersect_ext_config (NMDevice *self,
+                      AppliedConfig *config,
+                      gboolean intersect_routes)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMIPConfig *ext;
@@ -12538,10 +12583,11 @@ intersect_ext_config (NMDevice *self, AppliedConfig *config)
 	      : (NMIPConfig *) priv->ext_ip_config_6;
 
 	if (config->current)
-		nm_ip_config_intersect (config->current, ext, penalty);
+		nm_ip_config_intersect (config->current, ext, intersect_routes, penalty);
 	else {
 		config->current = nm_ip_config_intersect_alloc (config->orig,
 		                                                ext,
+		                                                intersect_routes,
 		                                                penalty);
 	}
 }
@@ -12552,6 +12598,7 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int ifindex;
 	GSList *iter;
+	gboolean is_up;
 
 	nm_assert_addr_family (addr_family);
 
@@ -12559,6 +12606,8 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
 	if (!ifindex)
 		return FALSE;
 
+	is_up = nm_platform_link_is_up (nm_device_get_platform (self), ifindex);
+
 	if (addr_family == AF_INET) {
 
 		g_clear_object (&priv->ext_ip_config_4);
@@ -12573,14 +12622,15 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
 				 * by the user. */
 				if (priv->con_ip_config_4) {
 					nm_ip4_config_intersect (priv->con_ip_config_4, priv->ext_ip_config_4,
+					                         is_up,
 					                         default_route_metric_penalty_get (self, AF_INET));
 				}
 
-				intersect_ext_config (self, &priv->dev_ip4_config);
-				intersect_ext_config (self, &priv->wwan_ip_config_4);
+				intersect_ext_config (self, &priv->dev_ip4_config, is_up);
+				intersect_ext_config (self, &priv->wwan_ip_config_4, is_up);
 
 				for (iter = priv->vpn_configs_4; iter; iter = iter->next)
-					nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, 0);
+					nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, is_up, 0);
 			}
 
 			/* Remove parts from ext_ip_config_4 to only contain the information that
@@ -12624,15 +12674,16 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
 				 * by the user. */
 				if (priv->con_ip_config_6) {
 					nm_ip6_config_intersect (priv->con_ip_config_6, priv->ext_ip_config_6,
+					                         is_up,
 					                         default_route_metric_penalty_get (self, AF_INET6));
 				}
 
-				intersect_ext_config (self, &priv->ac_ip6_config);
-				intersect_ext_config (self, &priv->dhcp6.ip6_config);
-				intersect_ext_config (self, &priv->wwan_ip_config_6);
+				intersect_ext_config (self, &priv->ac_ip6_config, is_up);
+				intersect_ext_config (self, &priv->dhcp6.ip6_config, is_up);
+				intersect_ext_config (self, &priv->wwan_ip_config_6, is_up);
 
 				for (iter = priv->vpn_configs_6; iter; iter = iter->next)
-					nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, 0);
+					nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, is_up, 0);
 
 				if (   priv->ipv6ll_has
 				    && !nm_ip6_config_lookup_address (priv->ext_ip_config_6, &priv->ipv6ll_addr))
@@ -13516,6 +13567,19 @@ nm_device_update_metered (NMDevice *self)
 	}
 }
 
+static NMDeviceCheckDevAvailableFlags
+_device_check_dev_available_flags_from_con (NMDeviceCheckConAvailableFlags con_flags)
+{
+	NMDeviceCheckDevAvailableFlags dev_flags;
+
+	dev_flags = NM_DEVICE_CHECK_DEV_AVAILABLE_NONE;
+
+	if (NM_FLAGS_HAS (con_flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER))
+		dev_flags |= _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER;
+
+	return dev_flags;
+}
+
 static gboolean
 _nm_device_check_connection_available (NMDevice *self,
                                        NMConnection *connection,
@@ -13557,34 +13621,30 @@ _nm_device_check_connection_available (NMDevice *self,
 		return FALSE;
 	}
 	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
-		if (NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)) {
-			if (!nm_device_get_managed (self, TRUE)) {
+		if (!nm_device_get_managed (self, TRUE)) {
+			if (!nm_device_get_managed (self, FALSE)) {
 				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
-				                            "device is unmanaged");
+				                            "device is strictly unmanaged");
 				return FALSE;
 			}
-		} else {
-			if (!nm_device_get_managed (self, FALSE)) {
+			if (!NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED)) {
 				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
-				                            "device is unmanaged for interal request");
+				                            "device is currently unmanaged");
 				return FALSE;
 			}
 		}
 	}
 	if (   state < NM_DEVICE_STATE_DISCONNECTED
 	    && !nm_device_is_software (self)) {
-		if (NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)) {
-			if (!nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)) {
+		if (!nm_device_is_available (self, _device_check_dev_available_flags_from_con (flags))) {
+			if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)) {
 				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
 				                            "device is not available");
-				return FALSE;
-			}
-		} else {
-			if (!nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
+			} else {
 				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
 				                            "device is not available for internal request");
-				return FALSE;
 			}
+			return FALSE;
 		}
 	}
 
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 6342a657..8f376b20 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -167,14 +167,34 @@ typedef enum NMActStageReturn NMActStageReturn;
 typedef enum { /*< skip >*/
 	NM_DEVICE_CHECK_CON_AVAILABLE_NONE                                  = 0,
 
+	/* since NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST is a collection of flags with more fine grained
+	 * parts, this flag in general indicates that this is a user-request. */
 	_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST                     = (1L << 0),
+
+	/* we also consider devices which have no carrier but are still waiting for the driver
+	 * to detect carrier. Usually, such devices are not yet available, however for a user-request
+	 * they are. They might fail later if carrier doesn't come. */
 	_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER     = (1L << 1),
+
+	/* usually, a profile is only available if the Wi-Fi AP is in range. For an
+	 * explicit user request, we also consider profiles for APs that are not (yet)
+	 * visible. */
 	_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP           = (1L << 2),
+
+	/* a device can be marked as unmanaged for various reasons. Some of these reasons
+	 * are authorative, others not. Non-authoritative reasons can be overruled by
+	 * `nmcli device set $DEVICE managed yes`. Also, for an explicit user activation
+	 * request we may want to consider the device as managed. This flag makes devices
+	 * that are unmanaged appear available. */
+	_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED  = (1L << 3),
+
+	/* a collection of flags, that are commonly set for an explict user-request. */
 	NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST                      = _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST
 	                                                                    | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER
-	                                                                    | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP,
+	                                                                    | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP
+	                                                                    | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED,
 
-	NM_DEVICE_CHECK_CON_AVAILABLE_ALL                                   = (1L << 3) - 1,
+	NM_DEVICE_CHECK_CON_AVAILABLE_ALL                                   = (1L << 4) - 1,
 } NMDeviceCheckConAvailableFlags;
 
 struct _NMDevicePrivate;
@@ -190,7 +210,12 @@ struct _NMDevice {
 typedef enum { /*< skip >*/
 	NM_DEVICE_CHECK_DEV_AVAILABLE_NONE                                  = 0,
 
+	/* the device is considered available, even if it has no carrier.
+	 *
+	 * For various device types (software devices) we ignore carrier based
+	 * on the type. So, for them, this flag has no effect anyway. */
 	_NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER                       = (1L << 0),
+
 	NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST                      = _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER,
 
 	NM_DEVICE_CHECK_DEV_AVAILABLE_ALL                                   = (1L << 1) - 1,
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index 32d49916..1d1be742 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -229,7 +229,7 @@ vardict_from_network_type (const char *type)
 }
 
 static void
-insert_ap_from_network (GHashTable *aps, GDBusProxy *proxy, const char *path, int16_t signal, uint32_t ap_id)
+insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint32_t ap_id)
 {
 	gs_unref_object GDBusProxy *network_proxy = NULL;
 	gs_unref_variant GVariant *name_value = NULL, *type_value = NULL;
@@ -239,19 +239,12 @@ insert_ap_from_network (GHashTable *aps, GDBusProxy *proxy, const char *path, in
 	GVariant *rsn;
 	uint8_t bssid[6];
 	NMWifiAP *ap;
-	GError *error;
-
-	network_proxy = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy),
-	                                       G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                                       NULL,
-	                                       NM_IWD_SERVICE,
-	                                       path,
-	                                       NM_IWD_NETWORK_INTERFACE,
-	                                       NULL, &error);
-	if (!network_proxy) {
-		g_clear_error (&error);
+
+	network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (),
+	                                                   path,
+	                                                   NM_IWD_NETWORK_INTERFACE);
+	if (!network_proxy)
 		return;
-	}
 
 	name_value = g_dbus_proxy_get_cached_property (network_proxy, "Name");
 	type_value = g_dbus_proxy_get_cached_property (network_proxy, "Type");
@@ -344,10 +337,10 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 
 	if (compat) {
 		while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type))
-			insert_ap_from_network (new_aps, priv->dbus_station_proxy, path, signal, ap_id++);
+			insert_ap_from_network (new_aps, path, signal, ap_id++);
 	} else {
 		while (g_variant_iter_next (networks, "(&on)", &path, &signal))
-			insert_ap_from_network (new_aps, priv->dbus_station_proxy, path, signal, ap_id++);
+			insert_ap_from_network (new_aps, path, signal, ap_id++);
 	}
 
 	g_variant_iter_free (networks);
@@ -433,7 +426,7 @@ cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect)
 
 	set_current_ap (self, NULL, TRUE);
 
-	if (disconnect && priv->dbus_obj)
+	if (disconnect && priv->dbus_station_proxy)
 		send_disconnect (self);
 }
 
@@ -827,13 +820,8 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceIwd *self = NM_DEVICE_IWD (device);
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	gs_unref_variant GVariant *value = NULL;
-
-	if (!priv->enabled || !priv->dbus_obj)
-		return FALSE;
 
-	value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered");
-	return get_variant_boolean (value, "Powered");
+	return priv->enabled && priv->dbus_station_proxy;
 }
 
 static gboolean
@@ -970,8 +958,7 @@ dbus_request_scan_cb (NMDevice *device,
 
 	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 
-	if (   !priv->enabled
-	    || !priv->dbus_obj
+	if (   !priv->can_scan
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
 	    || nm_device_is_activating (device)) {
 		g_dbus_method_invocation_return_error_literal (context,
@@ -1012,8 +999,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self,
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
 
-	if (   !priv->enabled
-	    || !priv->dbus_obj
+	if (   !priv->can_scan
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
 	    || nm_device_is_activating (device)) {
 		g_dbus_method_invocation_return_error_literal (invocation,
@@ -1255,12 +1241,7 @@ secrets_error:
 	g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
 	                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
 	                                               "NM secrets request failed");
-
-	nm_device_state_changed (device,
-	                         NM_DEVICE_STATE_FAILED,
-	                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-
-	cleanup_association_attempt (self, TRUE);
+	/* Now wait for the Connect callback to update device state */
 }
 
 static void
@@ -1292,11 +1273,14 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 {
 	NMDeviceIwd *self = user_data;
 	NMDevice *device = NM_DEVICE (self);
+	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	gs_free_error GError *error = NULL;
 	NMConnection *connection;
 	NMSettingWireless *s_wifi;
 	GBytes *ssid;
 	gs_free char *ssid_utf8 = NULL;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED;
+	GVariant *value;
 
 	if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
 	                                 G_VARIANT_TYPE ("()"),
@@ -1311,6 +1295,12 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		       "Activation: (wifi) Network.Connect failed: %s",
 		       error->message);
 
+		if (nm_utils_error_is_cancelled (error, TRUE))
+			return;
+
+		if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH))
+			return;
+
 		connection = nm_device_get_applied_connection (device);
 		if (!connection)
 			goto failed;
@@ -1318,20 +1308,17 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR))
 			dbus_error = g_dbus_error_get_remote_error (error);
 
-		/* If secrets were wrong, we'd be getting a net.connman.iwd.Failed */
 		if (nm_streq0 (dbus_error, "net.connman.iwd.Failed")) {
 			nm_connection_clear_secrets (connection);
 
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-		} else if (   !nm_utils_error_is_cancelled (error, TRUE)
-		           && nm_device_is_activating (device))
-			goto failed;
-
-		/* Call Disconnect to make sure IWD's autoconnect is disabled */
-		cleanup_association_attempt (self, TRUE);
+			/* If secrets were wrong, we'd be getting a net.connman.iwd.Failed */
+			reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		} else if (nm_streq0 (dbus_error, "net.connman.iwd.Aborted")) {
+			/* If agent call was cancelled we'd be getting a net.connman.iwd.Aborted */
+			reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		}
 
-		return;
+		goto failed;
 	}
 
 	nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
@@ -1358,9 +1345,17 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	return;
 
 failed:
-	cleanup_association_attempt (self, FALSE);
-	nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
-	                       NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	/* Call Disconnect to make sure IWD's autoconnect is disabled */
+	cleanup_association_attempt (self, TRUE);
+
+	nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, reason);
+
+	value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State");
+	if (!priv->can_connect && nm_streq0 (get_variant_state (value), "disconnected")) {
+		priv->can_connect = true;
+		nm_device_emit_recheck_auto_activate (device);
+	}
+	g_variant_unref (value);
 }
 
 static void
@@ -1430,7 +1425,6 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	NMActRequest *req;
 	NMWifiAP *ap;
 	NMConnection *connection;
-	GError *error = NULL;
 	GDBusProxy *network_proxy;
 
 	req = nm_device_get_act_request (device);
@@ -1460,21 +1454,13 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		goto out;
 	}
 
-	/* Locate the IWD Network object */
-	network_proxy = g_dbus_proxy_new_for_bus_sync (NM_IWD_BUS_TYPE,
-	                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                                               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                                               NULL,
-	                                               NM_IWD_SERVICE,
-	                                               nm_wifi_ap_get_supplicant_path (ap),
-	                                               NM_IWD_NETWORK_INTERFACE,
-	                                               NULL, &error);
+	network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (),
+	                                                   nm_wifi_ap_get_supplicant_path (ap),
+	                                                   NM_IWD_NETWORK_INTERFACE);
 	if (!network_proxy) {
 		_LOGE (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) could not get Network interface proxy for %s: %s",
-		       nm_wifi_ap_get_supplicant_path (ap),
-		       error->message);
-		g_clear_error (&error);
+		       "Activation: (wifi) could not get Network interface proxy for %s",
+		       nm_wifi_ap_get_supplicant_path (ap));
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 		goto out;
 	}
@@ -1575,7 +1561,7 @@ device_state_changed (NMDevice *device,
 		 * transition to DISCONNECTED because the device is now
 		 * ready to use.
 		 */
-		if (priv->enabled && priv->dbus_obj) {
+		if (priv->enabled && priv->dbus_station_proxy) {
 			nm_device_queue_recheck_available (device,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
@@ -1632,10 +1618,11 @@ set_enabled (NMDevice *device, gboolean enabled)
 		if (state != NM_DEVICE_STATE_UNAVAILABLE)
 			_LOGW (LOGD_CORE, "not in expected unavailable state!");
 
-		if (priv->dbus_obj)
+		if (priv->dbus_station_proxy) {
 			nm_device_queue_recheck_available (device,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		}
 	} else {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_UNAVAILABLE,
@@ -1753,13 +1740,6 @@ state_changed (NMDeviceIwd *self, const char *new_state)
 	/* Don't allow scanning while connecting, disconnecting or roaming */
 	priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected");
 
-	/* Don't allow new connection until iwd exits disconnecting */
-	can_connect = NM_IN_STRSET (new_state, "disconnected");
-	if (can_connect != priv->can_connect) {
-		priv->can_connect = can_connect;
-		nm_device_emit_recheck_auto_activate (device);
-	}
-
 	if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) {
 		/* If we were connecting, do nothing, the confirmation of
 		 * a connection success is handled in the Device.Connect
@@ -1773,40 +1753,39 @@ state_changed (NMDeviceIwd *self, const char *new_state)
 		_LOGW (LOGD_DEVICE | LOGD_WIFI,
 		       "Unsolicited connection success, asking IWD to disconnect");
 		send_disconnect (self);
-
-		return;
 	} else if (NM_IN_STRSET (new_state, "disconnecting", "disconnected")) {
-		if (!iwd_connection)
-			return;
-
 		/* Call Disconnect on the IWD device object to make sure it
 		 * disables its own autoconnect.
-		 *
-		 * Note we could instead call net.connman.iwd.KnownNetworks.ForgetNetwork
-		 * and leave the device in autoconnect.  This way if NetworkManager
-		 * changes any settings for this connection, they'd be taken into
-		 * account on the next connection attempt.  But both methods are
-		 * a hack, we'll perhaps need an IWD API to "connect once" without
-		 * storing anything.
 		 */
 		send_disconnect (self);
 
 		/*
-		 * If IWD is still handling the Connect call, let our callback
-		 * for the dbus method handle the failure.
+		 * If IWD is still handling the Connect call, let our Connect
+		 * callback for the dbus method handle the failure.  The main
+		 * reason we can't handle the failure here is because the method
+		 * callback will have more information on the specific failure
+		 * reason.
 		 */
 		if (dev_state == NM_DEVICE_STATE_CONFIG)
 			return;
 
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-	                                 NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
-
-		return;
-	} else if (nm_streq (new_state, "unknown"))
+		if (iwd_connection)
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+	} else if (!nm_streq (new_state, "unknown")) {
+		_LOGE (LOGD_WIFI, "State %s unknown", new_state);
 		return;
+	}
 
-	_LOGE (LOGD_WIFI, "State %s unknown", new_state);
+	/* Don't allow new connection until iwd exits disconnecting and no
+	 * Connect callback is pending.
+	 */
+	can_connect = NM_IN_STRSET (new_state, "disconnected");
+	if (can_connect != priv->can_connect) {
+		priv->can_connect = can_connect;
+		nm_device_emit_recheck_auto_activate (device);
+	}
 }
 
 static void
@@ -1856,9 +1835,72 @@ station_properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
 static void
 powered_changed (NMDeviceIwd *self, gboolean new_powered)
 {
+	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
 	nm_device_queue_recheck_available (NM_DEVICE (self),
 	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+
+	if (new_powered) {
+		GDBusInterface *interface;
+		GVariant *value;
+
+		if (priv->dbus_station_proxy)
+			return;
+
+		interface = g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_STATION_INTERFACE);
+		if (!interface) {
+			/* No Station interface on the device object.  Check if the
+			 * "State" property is present on the Device interface, that
+			 * would mean we're dealing with an IWD version from before the
+			 * Device/Station split (0.7 or earlier) and we can easily
+			 * handle that by making priv->dbus_device_proxy and
+			 * priv->dbus_station_proxy both point at the Device interface.
+			 */
+			value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State");
+			if (!value) {
+				_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
+				       NM_IWD_STATION_INTERFACE,
+				       g_dbus_object_get_object_path (priv->dbus_obj));
+				return;
+			}
+
+			g_variant_unref (value);
+			interface = g_object_ref (priv->dbus_device_proxy);
+		}
+
+		priv->dbus_station_proxy = G_DBUS_PROXY (interface);
+
+		value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "Scanning");
+		priv->scanning = get_variant_boolean (value, "Scanning");
+		g_variant_unref (value);
+
+		value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State");
+		state_changed (self, get_variant_state (value));
+		g_variant_unref (value);
+
+		g_signal_connect (priv->dbus_station_proxy, "g-properties-changed",
+		                  G_CALLBACK (station_properties_changed), self);
+
+		/* Call Disconnect to make sure IWD's autoconnect is disabled.
+		 * Autoconnect is the default state after device is brought UP.
+		 */
+		if (priv->enabled)
+			send_disconnect (self);
+	} else {
+		if (!priv->dbus_station_proxy)
+			return;
+
+		g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy,
+		                                      station_properties_changed, self);
+		g_clear_object (&priv->dbus_station_proxy);
+
+		priv->can_scan = FALSE;
+		priv->scanning = FALSE;
+		priv->scan_requested = FALSE;
+		priv->can_connect = FALSE;
+		cleanup_association_attempt (self, FALSE);
+	}
 }
 
 static void
@@ -1887,37 +1929,35 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	GDBusInterface *interface;
 	GVariant *value;
+	gboolean powered;
 
 	if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object))
 		return;
 
+	if (priv->enabled && priv->dbus_station_proxy) {
+		nm_device_queue_recheck_available (NM_DEVICE (self),
+		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	}
+
 	if (priv->dbus_device_proxy) {
 		g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy,
 		                                      device_properties_changed, self);
 		g_clear_object (&priv->dbus_device_proxy);
-		g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy,
-		                                      station_properties_changed, self);
-		g_clear_object (&priv->dbus_station_proxy);
-	}
-
-	if (priv->enabled)
-		nm_device_queue_recheck_available (NM_DEVICE (self),
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 
-	if (!object) {
-		priv->can_scan = FALSE;
+		powered_changed (self, FALSE);
+	}
 
-		cleanup_association_attempt (self, FALSE);
+	if (!object)
 		return;
-	}
 
 	interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE);
 	if (!interface) {
 		_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
 		       NM_IWD_DEVICE_INTERFACE,
 		       g_dbus_object_get_object_path (object));
-		goto error;
+		g_clear_object (&priv->dbus_obj);
+		return;
 	}
 
 	priv->dbus_device_proxy = G_DBUS_PROXY (interface);
@@ -1925,60 +1965,14 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
 	g_signal_connect (priv->dbus_device_proxy, "g-properties-changed",
 	                  G_CALLBACK (device_properties_changed), self);
 
-	interface = g_dbus_object_get_interface (object, NM_IWD_STATION_INTERFACE);
-	if (!interface) {
-		/* No Station interface on the device object.  Check if the
-		 * "State" property is present on the Device interface, that
-		 * would mean we're dealing with an IWD version from before the
-		 * Device/Station split (0.7 or earlier) and we can easily
-		 * handle that by making priv->dbus_device_proxy and
-		 * priv->dbus_station_proxy both point at the Device interface.
-		 *
-		 * TODO: handle device in a mode initially other than station
-		 * -- also means the Station interface won't be there.
-		 */
-		value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State");
-		g_variant_unref (value);
-		if (!value) {
-			_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
-			       NM_IWD_STATION_INTERFACE,
-			       g_dbus_object_get_object_path (object));
-			goto error;
-		}
-
-		interface = g_object_ref (priv->dbus_device_proxy);
-	}
-
-	priv->dbus_station_proxy = G_DBUS_PROXY (interface);
-
-	value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "Scanning");
-	priv->scanning = get_variant_boolean (value, "Scanning");
-	g_variant_unref (value);
-	priv->scan_requested = FALSE;
-
-	value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State");
-	state_changed (self, get_variant_state (value));
+	value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered");
+	powered = get_variant_boolean (value, "Powered");
 	g_variant_unref (value);
 
-	g_signal_connect (priv->dbus_station_proxy, "g-properties-changed",
-	                  G_CALLBACK (station_properties_changed), self);
-
-	set_powered (self, priv->enabled);
-
-	/* Call Disconnect to make sure IWD's autoconnect is disabled.
-	 * Autoconnect is the default state after device is brought UP.
-	 */
-	if (priv->enabled)
-		send_disconnect (self);
-
-	return;
-error:
-	g_clear_object (&priv->dbus_obj);
-	if (priv->dbus_device_proxy) {
-		g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy,
-		                                      device_properties_changed, self);
-		g_clear_object (&priv->dbus_device_proxy);
-	}
+	if (powered != priv->enabled)
+		set_powered (self, priv->enabled);
+	else if (powered)
+		powered_changed (self, TRUE);
 }
 
 gboolean
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index 6f833635..9ae81139 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -279,7 +279,7 @@ companion_state_changed_cb (NMDeviceWifi *companion,
 }
 
 static gboolean
-companion_scan_prohibited_cb (NMDeviceWifi *companion, gpointer user_data)
+companion_scan_prohibited_cb (NMDeviceWifi *companion, gboolean periodic, gpointer user_data)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
 	NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
@@ -346,7 +346,7 @@ device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data)
 		nm_device_queue_recheck_available (NM_DEVICE (self),
 		                                   NM_DEVICE_STATE_REASON_NONE,
 		                                   NM_DEVICE_STATE_REASON_NONE);
-		nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, TRUE);
+		nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, FALSE);
 	}
 }
 
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index ec1150bb..2ce84618 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -96,6 +96,7 @@ typedef struct {
 	bool              requested_scan:1;
 	bool              ssid_found:1;
 	bool              is_scanning:1;
+	bool              hidden_probe_scan_warn:1;
 
 	gint64            last_scan; /* milliseconds */
 	gint32            scheduled_scan_time; /* seconds */
@@ -1349,8 +1350,17 @@ request_wireless_scan (NMDeviceWifi *self,
 
 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested");
 
-		if (!ssids)
-			ssids = hidden_ssids = build_hidden_probe_list (self);
+		if (!ssids) {
+			hidden_ssids = build_hidden_probe_list (self);
+			if (hidden_ssids) {
+				if (priv->hidden_probe_scan_warn) {
+					priv->hidden_probe_scan_warn = FALSE;
+					_LOGW (LOGD_WIFI, "wifi-scan: active scanning for networks due to profiles with wifi.hidden=yes. This makes you trackable");
+				}
+				ssids = hidden_ssids;
+			} else
+				priv->hidden_probe_scan_warn = TRUE;
+		}
 
 		if (_LOGD_ENABLED (LOGD_WIFI)) {
 			if (ssids) {
@@ -3244,6 +3254,7 @@ nm_device_wifi_init (NMDeviceWifi *self)
 
 	c_list_init (&priv->aps_lst_head);
 
+	priv->hidden_probe_scan_warn = TRUE;
 	priv->mode = NM_802_11_MODE_INFRA;
 	priv->wowlan_restore = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
 }
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index 1f28d263..a3da9791 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -430,7 +430,16 @@ mirror_8021x_connection (NMIwdManager *self,
 	                                    NULL));
 	nm_connection_add_setting (connection, setting);
 
-	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_802_1X, NULL));
+	/* "password" and "private-key-password" may be requested by the IWD agent
+	 * from NM and IWD will implement a specific secret cache policy so by
+	 * default respect that policy and don't save copies of those secrets in
+	 * NM settings.  The saved values can not be used anyway because of our
+	 * use of NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW.
+	 */
+	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_802_1X,
+	                                    NM_SETTING_802_1X_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	                                    NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	                                    NULL));
 	nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), "external");
 	nm_connection_add_setting (connection, setting);
 
@@ -786,6 +795,21 @@ nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name,
 	return g_hash_table_contains (priv->known_networks, &kn_id);
 }
 
+GDBusProxy *
+nm_iwd_manager_get_dbus_interface (NMIwdManager *self, const char *path,
+                                   const char *name)
+{
+	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+	GDBusInterface *interface;
+
+	if (!priv->object_manager)
+		return NULL;
+
+	interface = g_dbus_object_manager_get_interface (priv->object_manager, path, name);
+
+	return interface ? G_DBUS_PROXY (interface) : NULL;
+}
+
 /*****************************************************************************/
 
 NM_DEFINE_SINGLETON_GETTER (NMIwdManager, nm_iwd_manager_get,
diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h
index cf0e7262..57b7007a 100644
--- a/src/devices/wifi/nm-iwd-manager.h
+++ b/src/devices/wifi/nm-iwd-manager.h
@@ -57,4 +57,7 @@ NMIwdManager *nm_iwd_manager_get (void);
 gboolean nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name,
                                           NMIwdNetworkSecurity security);
 
+GDBusProxy *nm_iwd_manager_get_dbus_interface (NMIwdManager *self, const char *path,
+                                               const char *name);
+
 #endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 253574d5..04cb8599 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -1063,7 +1063,7 @@ stage3_ip6_done (NMModemBroadband *self)
 
 	/* DNS servers */
 	dns = mm_bearer_ip_config_get_dns (self->_priv.ipv6_config);
-	for (i = 0; dns[i]; i++) {
+	for (i = 0; dns && dns[i]; i++) {
 		struct in6_addr addr;
 
 		if (inet_pton (AF_INET6, dns[i], &addr)) {
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
index d8f8dd98..a2c3bfb6 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp/nm-dhcp-dhclient-utils.c
@@ -444,6 +444,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 		add_request (reqs, "static-routes");
 		add_request (reqs, "wpad");
 		add_request (reqs, "ntp-servers");
+		add_request (reqs, "root-path");
 	} else {
 		add_hostname6 (new_contents, hostname);
 		add_request (reqs, "dhcp6.name-servers");
diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c
index 46d2339b..4a936178 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -41,6 +41,7 @@
 #include "nm-utils/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"
@@ -120,8 +121,20 @@ get_dhclient_leasefile (int addr_family,
                         const char *uuid,
                         char **out_preferred_path)
 {
+	char *rundir_path;
 	char *path;
 
+	/* 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 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),
@@ -133,6 +146,12 @@ get_dhclient_leasefile (int addr_family,
 		return path;
 	}
 
+	if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) {
+		g_free (path);
+		path = rundir_path;
+	} else {
+		g_free (rundir_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
diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c
index 2b48d7a5..7f1d2a7b 100644
--- a/src/dhcp/nm-dhcp-helper.c
+++ b/src/dhcp/nm-dhcp-helper.c
@@ -118,7 +118,7 @@ kill_pid (void)
 	if (pid_str)
 		pid = strtol (pid_str, NULL, 10);
 	if (pid) {
-		_LOGI ("a fatal error occured, kill dhclient instance with pid %d\n", pid);
+		_LOGI ("a fatal error occurred, kill dhclient instance with pid %d", pid);
 		kill (pid, SIGTERM);
 	}
 }
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index 6c71af9d..5ae16d72 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -413,7 +413,7 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 	                                        NM_CONFIG_KEYFILE_KEY_MAIN_DHCP,
 	                                        NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
 	client = client_free;
-	if (nm_config_get_configure_and_quit (config)) {
+	if (nm_config_get_configure_and_quit (config) == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) {
 		client_factory = &_nm_dhcp_client_factory_internal;
 		if (client && !nm_streq (client, client_factory->name))
 			nm_log_info (LOGD_DHCP, "dhcp-init: Using internal DHCP client since configure-and-quit is set.");
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index b51c6e7b..5b7b5fbe 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -32,6 +32,7 @@
 #include "nm-utils/unaligned.h"
 
 #include "nm-utils.h"
+#include "nm-config.h"
 #include "nm-dhcp-utils.h"
 #include "NetworkManagerUtils.h"
 #include "platform/nm-platform.h"
@@ -121,6 +122,7 @@ static const ReqOption dhcp4_requests[] = {
 	{ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,         REQPREFIX "rfc3442_classless_static_routes", TRUE },
 	{ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, REQPREFIX "ms_classless_static_routes",      TRUE },
 	{ SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY,    REQPREFIX "wpad",                            TRUE },
+	{ SD_DHCP_OPTION_ROOT_PATH,                      REQPREFIX "root_path",                       TRUE },
 
 	/* Internal values */
 	{ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME,          REQPREFIX "expiry",                          FALSE },
@@ -432,6 +434,13 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 		add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, str->str);
 	}
 
+	/* Root path */
+	r = sd_dhcp_lease_get_root_path (lease, &s);
+	if (r >= 0) {
+		LOG_LEASE (LOGD_DHCP4, "root path '%s'", s);
+		add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROOT_PATH, s);
+	}
+
 	r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len);
 	if (r >= 0)
 		metered = !!memmem (data, data_len, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED"));
@@ -445,10 +454,30 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 static char *
 get_leasefile_path (int addr_family, const char *iface, const char *uuid)
 {
-	return g_strdup_printf (NMSTATEDIR "/internal%s-%s-%s.lease",
-	                        addr_family == AF_INET6 ? "6" : "",
-	                        uuid,
-	                        iface);
+	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;
+	}
 }
 
 /*****************************************************************************/
@@ -897,7 +926,7 @@ ip6_start (NMDhcpClient *client,
 	}
 
 	if (needed_prefixes > 0) {
-		_LOGW ("dhcp-client6: prefix delegation not yet supported, won't supply %d prefixes\n",
+		_LOGW ("dhcp-client6: prefix delegation not yet supported, won't supply %d prefixes",
 		       needed_prefixes);
 	}
 
diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
index 14a1c786..edac4257 100644
--- a/src/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/dhcp/tests/test-dhcp-dhclient.c
@@ -104,6 +104,7 @@ static const char *orig_missing_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -132,6 +133,7 @@ static const char *override_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -161,6 +163,7 @@ static const char *quote_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -190,6 +193,7 @@ static const char *quote_client_id_expected_2 = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -219,6 +223,7 @@ static const char *hex_zero_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -248,6 +253,7 @@ static const char *ascii_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -277,6 +283,7 @@ static const char *hex_single_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -310,6 +317,7 @@ static const char *existing_hex_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -347,6 +355,7 @@ static const char *existing_escaped_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -385,6 +394,7 @@ static const char *existing_ascii_client_id_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -419,7 +429,8 @@ static const char *fqdn_expected = \
 	"also request ms-classless-static-routes;\n"
 	"also request static-routes;\n"
 	"also request wpad;\n"
-	"also request ntp-servers;\n\n";
+	"also request ntp-servers;\n"
+	"also request root-path;\n\n";
 
 static void
 test_fqdn (void)
@@ -455,6 +466,7 @@ static const char *fqdn_options_override_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n"
 	"# FQDN options from /path/to/dhclient.conf\n"
 	"send fqdn.encoded off;\n"
@@ -492,6 +504,7 @@ static const char *override_hostname_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -580,6 +593,7 @@ static const char *existing_alsoreq_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -618,6 +632,7 @@ static const char *existing_req_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -657,6 +672,7 @@ static const char *existing_multiline_alsoreq_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -890,6 +906,7 @@ static const char *interface1_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -935,6 +952,7 @@ static const char *interface2_expected = \
 	"also request static-routes;\n"
 	"also request wpad;\n"
 	"also request ntp-servers;\n"
+	"also request root-path;\n"
 	"\n";
 
 static void
@@ -1030,6 +1048,7 @@ test_structured (void)
 		"also request static-routes;\n"
 		"also request wpad;\n"
 		"also request ntp-servers;\n"
+		"also request root-path;\n"
 		"\n";
 
 	new_client_id = g_bytes_new (bytes, sizeof (bytes) - 1);
@@ -1085,6 +1104,7 @@ test_config_req_intf (void)
 		"also request ms-classless-static-routes;\n"
 		"also request static-routes;\n"
 		"also request wpad;\n"
+		"also request root-path;\n"
 		"\n";
 
 	test_config (orig, expected,
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c
index cb7a626c..b48c6b87 100644
--- a/src/dns/nm-dns-dnsmasq.c
+++ b/src/dns/nm-dns-dnsmasq.c
@@ -306,8 +306,6 @@ start_dnsmasq (NMDnsDnsmasq *self)
 	const char *argv[15];
 	GPid pid = 0;
 	guint idx = 0;
-	NMDBusManager *dbus_mgr;
-	GDBusConnection *connection;
 
 	if (priv->running) {
 		/* the dnsmasq process is running. Nothing to do. */
@@ -358,22 +356,16 @@ start_dnsmasq (NMDnsDnsmasq *self)
 		return;
 	}
 
-	dbus_mgr = nm_dbus_manager_get ();
-	g_return_if_fail (dbus_mgr);
-
-	connection = nm_dbus_manager_get_connection (dbus_mgr);
-	g_return_if_fail (connection);
-
 	priv->dnsmasq_cancellable = g_cancellable_new ();
-	g_dbus_proxy_new (connection,
-	                  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);
+	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);
 }
 
 static gboolean
diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 544f19c1..10797b86 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -129,7 +129,7 @@ call_done (GObject *source, GAsyncResult *r, gpointer user_data)
 	if (!v) {
 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 			return;
-		_LOGW ("Failed: %s\n", error->message);
+		_LOGW ("Failed: %s", error->message);
 		g_error_free (error);
 	}
 }
@@ -391,28 +391,20 @@ static void
 nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self)
 {
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
-	NMDBusManager *dbus_mgr;
-	GDBusConnection *connection;
 
 	c_list_init (&priv->request_queue_lst_head);
 
-	dbus_mgr = nm_dbus_manager_get ();
-	g_return_if_fail (dbus_mgr);
-
-	connection = nm_dbus_manager_get_connection (dbus_mgr);
-	g_return_if_fail (connection);
-
 	priv->init_cancellable = g_cancellable_new ();
-	g_dbus_proxy_new (connection,
-	                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                  G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                  NULL,
-	                  SYSTEMD_RESOLVED_DBUS_SERVICE,
-	                  SYSTEMD_RESOLVED_DBUS_PATH,
-	                  SYSTEMD_RESOLVED_DBUS_SERVICE ".Manager",
-	                  priv->init_cancellable,
-	                  resolved_proxy_created,
-	                  self);
+	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,
+	                          SYSTEMD_RESOLVED_DBUS_SERVICE,
+	                          SYSTEMD_RESOLVED_DBUS_PATH,
+	                          SYSTEMD_RESOLVED_DBUS_SERVICE ".Manager",
+	                          priv->init_cancellable,
+	                          resolved_proxy_created,
+	                          self);
 }
 
 NMDnsPlugin *
diff --git a/src/initrd/nm-initrd-generator.c b/src/initrd/nm-initrd-generator.c
new file mode 100644
index 00000000..eb9a38df
--- /dev/null
+++ b/src/initrd/nm-initrd-generator.c
@@ -0,0 +1,139 @@
+/* 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 (C) 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+#include "nm-core-utils.h"
+#include "nm-core-internal.h"
+#include "nm-keyfile-internal.h"
+#include "nm-initrd-generator.h"
+#include "nm-utils/nm-io-utils.h"
+
+/*****************************************************************************/
+
+#define _NMLOG(level, domain, ...) \
+    nm_log ((level), (domain), NULL, NULL, \
+            "initrd-generator: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__) \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+static void
+output_conn (gpointer key, gpointer value, gpointer user_data)
+{
+	const char *basename = key;
+	NMConnection *connection = value;
+	char *connections_dir = user_data;
+	GKeyFile *file;
+	gs_free char *data = NULL;
+	GError *error = NULL;
+	gsize len;
+
+	if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
+		g_print ("%s\n", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	file = nm_keyfile_write (connection, NULL, NULL, &error);
+	if (file == NULL) {
+		g_print ("%s\n", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	data = g_key_file_to_data (file, &len, &error);
+	if (!data) {
+		g_print ("%s\n", error->message);
+		g_error_free (error);
+	} else if (connections_dir) {
+		gs_free char *basename_w_ext = g_strconcat (basename, ".nmconnection", NULL);
+		char *filename = g_build_filename (connections_dir, basename_w_ext, NULL);
+
+		if (!nm_utils_file_set_contents (filename, data, len, 0600, &error)) {
+			g_print ("%s\n", error->message);
+			g_error_free (error);
+		}
+		g_free (filename);
+	} else {
+		g_print ("\n*** Connection '%s' ***\n\n%s\n", basename, data);
+	}
+
+	g_key_file_free (file);
+}
+
+#define DEFAULT_CONNECTIONS_DIR  NMRUNDIR "/system-connections"
+#define DEFAULT_SYSFS_DIR        "/sys"
+
+int
+main (int argc, char *argv[])
+{
+	GHashTable *connections;
+	gs_free char *connections_dir = NULL;
+	gs_free char *sysfs_dir = NULL;
+	gboolean dump_to_stdout = FALSE;
+	gs_strfreev char **remaining = NULL;
+	GOptionEntry option_entries[] = {
+		{ "connections-dir", 'c', 0, G_OPTION_ARG_FILENAME, &connections_dir, "Output connection directory", DEFAULT_CONNECTIONS_DIR },
+		{ "sysfs-dir", 'd', 0, G_OPTION_ARG_FILENAME, &sysfs_dir, "The sysfs mount point", DEFAULT_SYSFS_DIR },
+		{ "stdout", 's', 0, G_OPTION_ARG_NONE, &dump_to_stdout, "Dump connections to standard output", NULL },
+		{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL, NULL },
+		{ NULL }
+	};
+	GOptionContext *option_context;
+	GError *error = NULL;
+
+	option_context = g_option_context_new ("-- [ip=...] [rd.route=...] [bridge=...] [bond=...] [team=...] [vlan=...] "
+	                                       "[bootdev=...] [nameserver=...] [rd.peerdns=...] [rd.bootif=...] [BOOTIF=...] ... ");
+
+	g_option_context_set_summary (option_context, "Generate early NetworkManager configuration.");
+	g_option_context_set_description (option_context,
+		"This tool scans the command line for options relevant to network\n"
+		"configuration and creates configuration files for an early instance\n"
+		"of NetworkManager run from the initial ramdisk during early boot.");
+	g_option_context_add_main_entries (option_context, option_entries, GETTEXT_PACKAGE);
+
+	if (!g_option_context_parse (option_context, &argc, &argv, &error)) {
+		_LOGW (LOGD_CORE, "%s\n", error->message);
+		return 1;
+	}
+
+	if (!remaining) {
+		/* No arguments, no networking. Don't bother. */
+		return 0;
+	}
+
+	if (!connections_dir)
+		connections_dir = g_strdup (DEFAULT_CONNECTIONS_DIR);
+	if (!sysfs_dir)
+		sysfs_dir = g_strdup (DEFAULT_SYSFS_DIR);
+	if (dump_to_stdout)
+		g_clear_pointer (&connections_dir, g_free);
+
+	if (connections_dir && g_mkdir_with_parents (connections_dir, 0755) != 0) {
+		_LOGW (LOGD_CORE, "%s: %s\n", connections_dir, strerror (errno));
+		return 1;
+	}
+
+	connections = nmi_cmdline_reader_parse (sysfs_dir, remaining);
+	g_hash_table_foreach (connections, output_conn, connections_dir);
+	g_hash_table_destroy (connections);
+
+	return 0;
+}
diff --git a/src/initrd/nm-initrd-generator.h b/src/initrd/nm-initrd-generator.h
new file mode 100644
index 00000000..1fa858fc
--- /dev/null
+++ b/src/initrd/nm-initrd-generator.h
@@ -0,0 +1,45 @@
+/* NetworkManager initrd configuration generator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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, 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_INITRD_GENERATOR_H__
+#define __NM_INITRD_GENERATOR_H__
+
+#include "nm-connection.h"
+#include "nm-utils.h"
+
+static inline gboolean
+guess_ip_address_family (const char *str)
+{
+	if (str == NULL)
+		return AF_UNSPEC;
+	else if (strchr (str, '.'))
+		return AF_INET;
+	else if (strchr (str, ':'))
+		return AF_INET6;
+	else
+		return AF_UNSPEC;
+}
+
+GHashTable *nmi_ibft_read (const char *sysfs_dir);
+
+gboolean nmi_ibft_update_connection_from_nic (NMConnection *connection, GHashTable *nic, GError **error);
+
+GHashTable *nmi_cmdline_reader_parse (const char *sysfs_dir, char **argv);
+
+#endif  /* __NM_INITRD_GENERATOR_H__ */
diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c
new file mode 100644
index 00000000..2cef534e
--- /dev/null
+++ b/src/initrd/nmi-cmdline-reader.c
@@ -0,0 +1,714 @@
+/* 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 (C) 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+#include "nm-core-internal.h"
+
+#include "nm-initrd-generator.h"
+
+#include <string.h>
+
+/*****************************************************************************/
+
+#define _NMLOG(level, domain, ...) \
+    nm_log ((level), (domain), NULL, NULL, \
+            "cmdline-reader: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__) \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+static NMConnection *
+get_conn (GHashTable *connections, const char *ifname, const char *type_name)
+{
+	NMConnection *connection;
+	NMSetting *setting;
+	const char *basename = ifname ?: "default_connection";
+
+	connection = g_hash_table_lookup (connections, (gpointer)basename);
+
+	if (connection) {
+		setting = (NMSetting *)nm_connection_get_setting_connection (connection);
+	} else {
+		connection = nm_simple_connection_new ();
+		g_hash_table_insert (connections, g_strdup (basename), connection);
+
+		/* Start off assuming dynamic IP configurations. */
+
+		setting = nm_setting_ip4_config_new ();
+		nm_connection_add_setting (connection, setting);
+		g_object_set (setting,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+		              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+		              NULL);
+
+		setting = nm_setting_ip6_config_new ();
+		nm_connection_add_setting (connection, setting);
+		g_object_set (setting,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+		              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+		              NULL);
+
+		setting = nm_setting_connection_new ();
+		nm_connection_add_setting (connection, setting);
+		g_object_set (setting,
+		              NM_SETTING_CONNECTION_ID, ifname ?: "Wired Connection",
+		              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+		              NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
+		              NULL);
+
+		if (!type_name)
+			type_name = NM_SETTING_WIRED_SETTING_NAME;
+	}
+
+	if (type_name) {
+		g_object_set (setting, NM_SETTING_CONNECTION_TYPE, type_name, NULL);
+		if (!nm_connection_get_setting_by_name (connection, type_name)) {
+			setting = g_object_new (nm_setting_lookup_type (type_name), NULL);
+			nm_connection_add_setting (connection, setting);
+		}
+	}
+
+	return connection;
+}
+
+static char *
+get_word (char **argument, const char separator)
+{
+	char *word;
+	int nest = 0;
+
+	if (*argument == NULL)
+		return NULL;
+
+	if (**argument == '[') {
+		nest++;
+		(*argument)++;
+	}
+
+	word = *argument;
+
+	while (**argument != '\0') {
+		if (nest && **argument == ']') {
+			**argument = '\0';
+			(*argument)++;
+			nest--;
+			continue;
+		}
+
+		if (nest == 0 && **argument == separator) {
+			**argument = '\0';
+			(*argument)++;
+			break;
+		}
+		(*argument)++;
+	}
+
+	return *word ? word : NULL;
+}
+
+static void
+_base_setting_set (NMConnection *connection, const char *property, const char *value)
+{
+	NMSetting *setting;
+	const char *type_name = nm_connection_get_connection_type (connection);
+	GObjectClass *object_class = g_type_class_ref (nm_setting_lookup_type (type_name));
+	GParamSpec *spec = g_object_class_find_property (object_class, property);
+
+	if (!spec) {
+		_LOGW (LOGD_CORE, "'%s' does not support setting %s\n", type_name, property);
+		return;
+	}
+
+	setting = nm_connection_get_setting_by_name (connection, type_name);
+
+	if (G_IS_PARAM_SPEC_UINT (spec))
+		g_object_set (setting, property, g_ascii_strtoull (value, NULL, 10), NULL);
+	else if (G_IS_PARAM_SPEC_STRING (spec))
+		g_object_set (setting, property, value, NULL);
+	else
+		_LOGW (LOGD_CORE, "Don't know how to set '%s' of %s\n", property, type_name);
+
+	g_type_class_unref (object_class);
+}
+
+static void
+parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
+{
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip4 = NULL, *s_ip6 = NULL;
+	gs_unref_hashtable GHashTable *ibft = NULL;
+	const char *tmp;
+	const char *kind = NULL;
+	const char *client_ip = NULL;
+	const char *peer = NULL;
+	const char *gateway_ip = NULL;
+	const char *netmask = NULL;
+	const char *client_hostname = NULL;
+	const char *ifname = NULL;
+	const char *mtu = NULL;
+	const char *macaddr = NULL;
+	int client_ip_family = AF_UNSPEC;
+	int client_ip_prefix = -1;
+	const char *dns[2] = { 0, };
+	int dns_addr_family[2] = { 0, };
+	int i;
+	GError *error = NULL;
+
+	if (!*argument)
+		return;
+
+	tmp = get_word (&argument, ':');
+	if (!*argument) {
+		/* ip={dhcp|on|any|dhcp6|auto6|ibft} */
+		kind = tmp;
+	} else {
+		client_ip_family = guess_ip_address_family (tmp);
+		if (client_ip_family != AF_UNSPEC) {
+			/* <client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>: */
+			client_ip = tmp;
+			peer = get_word (&argument, ':');
+			gateway_ip = get_word (&argument, ':');
+			netmask = get_word (&argument, ':');
+			client_hostname = get_word (&argument, ':');
+			ifname = get_word (&argument, ':');
+		} else {
+			ifname = tmp;
+		}
+
+		/* <ifname>:{none|off|dhcp|on|any|dhcp6|auto6|ibft} */
+
+		kind = get_word (&argument, ':');
+
+		tmp = get_word (&argument, ':');
+		dns_addr_family[0] = guess_ip_address_family (tmp);
+		if (dns_addr_family[0] != AF_UNSPEC) {
+			dns[0] = tmp;
+			dns[1] = get_word (&argument, ':');
+			dns_addr_family[1] = guess_ip_address_family (dns[1]);
+			if (argument && *argument)
+				_LOGW (LOGD_CORE, "Ignoring extra: '%s'.\n", argument);
+		} else {
+			mtu = tmp;
+			macaddr = argument;
+		}
+	}
+
+	if (ifname == NULL && g_strcmp0 (kind, "ibft") == 0) {
+		GHashTableIter iter;
+		const char *mac;
+		GHashTable *nic;
+		const char *index;
+
+		/* This is the ip=ibft case. Just take all we got from iBFT
+		 * and don't process anything else, since there's no ifname
+		 * specified to apply it to. */
+		if (!ibft)
+			ibft = nmi_ibft_read (sysfs_dir);
+
+		g_hash_table_iter_init (&iter, ibft);
+		while (g_hash_table_iter_next (&iter, (gpointer)&mac, (gpointer)&nic)) {
+			connection = nm_simple_connection_new ();
+
+			index = g_hash_table_lookup (nic, "index");
+			if (!index) {
+				_LOGW (LOGD_CORE, "Ignoring an iBFT entry without an index\n");
+				continue;
+			}
+
+			if (!nmi_ibft_update_connection_from_nic (connection, nic, &error)) {
+				_LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s\n", error->message);
+				g_error_free (error);
+			}
+
+			g_hash_table_insert (connections,
+			                     g_strdup_printf ("ibft%s", index),
+			                     connection);
+		}
+
+		return;
+	}
+
+	/* Parsing done, construct the NMConnection. */
+	connection = get_conn (connections, ifname, NULL);
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+
+	if (netmask && *netmask) {
+		NMIPAddr addr;
+
+		if (nm_utils_parse_inaddr_bin (AF_INET, netmask, &addr)) {
+			client_ip_prefix = nm_utils_ip4_netmask_to_prefix (addr.addr4);
+		} else {
+			_LOGW (LOGD_CORE, "Unrecognized address: %s\n", client_ip);
+		}
+	}
+
+	/* Static IP configuration might be present. */
+	if (client_ip && *client_ip) {
+		NMIPAddress *address = NULL;
+		NMIPAddr addr;
+
+		if (nm_utils_parse_inaddr_prefix_bin (client_ip_family, client_ip, &addr,
+		                                      client_ip_prefix == -1 ? &client_ip_prefix : NULL)) {
+			if (client_ip_prefix == -1) {
+				switch (client_ip_family) {
+				case AF_INET:
+					client_ip_prefix = _nm_utils_ip4_get_default_prefix (addr.addr4);
+					break;
+				case AF_INET6:
+					client_ip_prefix = 64;
+					break;
+				}
+			}
+
+			address = nm_ip_address_new_binary (client_ip_family, &addr.addr_ptr, client_ip_prefix, &error);
+			if (!address) {
+				_LOGW (LOGD_CORE, "Invalid address '%s': %s\n", client_ip, error->message);
+				g_clear_error (&error);
+			}
+		} else {
+			_LOGW (LOGD_CORE, "Unrecognized address: %s\n", client_ip);
+		}
+
+		if (address) {
+			switch (client_ip_family) {
+			case AF_INET:
+				g_object_set (s_ip4,
+				              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+				              NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
+				              NULL);
+				nm_setting_ip_config_add_address (s_ip4, address);
+				break;
+			case AF_INET6:
+				g_object_set (s_ip6,
+				              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+				              NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
+				              NULL);
+				nm_setting_ip_config_add_address (s_ip6, address);
+				break;
+			default:
+				_LOGW (LOGD_CORE, "Unknown address family: %s\n", client_ip);
+				break;
+			}
+			nm_ip_address_unref (address);
+		}
+	}
+
+	/* Dynamic IP configuration configured explicitly. */
+	if (g_strcmp0 (kind, "none") == 0 || (g_strcmp0 (kind, "off") == 0)) {
+		if (nm_setting_ip_config_get_num_addresses (s_ip6) == 0) {
+			g_object_set (s_ip6,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+			              NULL);
+		}
+		if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
+			g_object_set (s_ip4,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+			              NULL);
+		}
+	} else if (g_strcmp0 (kind, "dhcp") == 0) {
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+		              NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
+		              NULL);
+		if (nm_setting_ip_config_get_num_addresses (s_ip6) == 0) {
+			g_object_set (s_ip6,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+			              NULL);
+		}
+	} else if (g_strcmp0 (kind, "dhcp6") == 0) {
+		g_object_set (s_ip6,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DHCP,
+		              NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
+		              NULL);
+		if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
+			g_object_set (s_ip4,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+			              NULL);
+		}
+	} else if (g_strcmp0 (kind, "auto6") == 0) {
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
+		              NULL);
+		if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
+			g_object_set (s_ip4,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+			              NULL);
+		}
+	} else if (g_strcmp0 (kind, "ibft") == 0) {
+		gs_free char *address_path = g_build_filename (sysfs_dir, "class", "net", ifname, "address", NULL);
+		gs_free char *mac, *mac_up = NULL;
+		GHashTable *nic = NULL;
+
+		if (!g_file_get_contents (address_path, &mac, NULL, &error)) {
+			_LOGW (LOGD_CORE, "Can't get a MAC address for %s: %s", ifname, error->message);
+			g_clear_error (&error);
+		}
+
+		if (mac) {
+			g_strchomp (mac);
+			mac_up = g_ascii_strup (mac, -1);
+			if (!ibft)
+				ibft = nmi_ibft_read (sysfs_dir);
+			nic = g_hash_table_lookup (ibft, mac_up);
+			if (!nic)
+				_LOGW (LOGD_CORE, "No iBFT NIC for %s (%s)\n", ifname, mac_up);
+		}
+
+		if (nic) {
+			if (!nmi_ibft_update_connection_from_nic (connection, nic, &error)) {
+				_LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s\n", error->message);
+				g_clear_error (&error);
+			}
+		}
+	}
+
+	if (peer && *peer)
+		_LOGW (LOGD_CORE, "Ignoring peer: %s (not implemented)\b", peer);
+
+	if (gateway_ip && *gateway_ip) {
+		int addr_family = guess_ip_address_family (gateway_ip);
+
+		if (nm_utils_ipaddr_valid (addr_family, gateway_ip)) {
+			switch (addr_family) {
+			case AF_INET:
+				g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL);
+				break;
+			case AF_INET6:
+				g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL);
+				break;
+			default:
+				_LOGW (LOGD_CORE, "Unknown address family: %s\n", gateway_ip);
+				break;
+			}
+		} else {
+			_LOGW (LOGD_CORE, "Invalid gateway: %s\n", gateway_ip);
+		}
+	}
+
+	if (client_hostname && *client_hostname) {
+		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, client_hostname, NULL);
+		g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, client_hostname, NULL);
+	}
+
+	for (i = 0; i < 2; i++) {
+		if (dns_addr_family[i] == AF_UNSPEC)
+			break;
+		if (nm_utils_ipaddr_valid (dns_addr_family[i], dns[i])) {
+			switch (dns_addr_family[i]) {
+			case AF_INET:
+				nm_setting_ip_config_add_dns (s_ip4, dns[i]);
+				break;
+			case AF_INET6:
+				nm_setting_ip_config_add_dns (s_ip6, dns[i]);
+				break;
+			default:
+				_LOGW (LOGD_CORE, "Unknown address family: %s\n", dns[i]);
+				break;
+			}
+		} else {
+			_LOGW (LOGD_CORE, "Invalid name server: %s\n", dns[i]);
+		}
+	}
+
+	if (mtu && *mtu)
+		_base_setting_set (connection, "mtu", mtu);
+
+	if (macaddr && *macaddr)
+		_base_setting_set (connection, "cloned-mac-address", macaddr);
+}
+
+static void
+parse_master (GHashTable *connections, char *argument, const char *type_name)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingBond *s_bond;
+	gs_free char *master_to_free = NULL;
+	const char *master;
+	char *slaves;
+	const char *slave;
+	char *opts;
+	char *opt;
+	const char *opt_name;
+	const char *mtu = NULL;
+
+	master = get_word (&argument, ':');
+	if (!master)
+		master = master_to_free = g_strdup_printf ("%s0", type_name);
+	slaves = get_word (&argument, ':');
+
+	connection = get_conn (connections, master, type_name);
+	s_con = nm_connection_get_setting_connection (connection);
+	master = nm_setting_connection_get_uuid (s_con);
+
+	if (strcmp (type_name, NM_SETTING_BOND_SETTING_NAME) == 0) {
+		s_bond = (NMSettingBond *)nm_connection_get_setting_by_name (connection, type_name);
+
+		opts = get_word (&argument, ':');
+		while (opts && *opts) {
+			opt = get_word (&opts, ',');
+			opt_name = get_word (&opt, '=');
+			nm_setting_bond_add_option (s_bond, opt_name, opt);
+		}
+
+		mtu = get_word (&argument, ':');
+	}
+
+	do {
+		slave = get_word (&slaves, ',');
+		if (slave == NULL)
+			slave = "eth0";
+
+		connection = get_conn (connections, slave, NULL);
+		s_con = nm_connection_get_setting_connection (connection);
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, type_name,
+		              NM_SETTING_CONNECTION_MASTER, master,
+		              NULL);
+		if (mtu)
+			_base_setting_set (connection, "mtu", mtu);
+	} while (slaves && *slaves != '\0');
+
+	if (argument && *argument)
+		_LOGW (LOGD_CORE, "Ignoring extra: '%s'.\n", argument);
+}
+
+static void
+parse_rd_route (GHashTable *connections, char *argument)
+{
+	NMConnection *connection;
+	const char *net;
+	const char *gateway;
+	const char *interface;
+	int family = AF_UNSPEC;
+	NMIPAddr net_addr = { 0, };
+	NMIPAddr gateway_addr = { 0, };
+	int net_prefix = -1;
+	NMIPRoute *route;
+	NMSettingIPConfig *s_ip;
+	GError *error = NULL;
+
+	net = get_word (&argument, ':');
+	gateway = get_word (&argument, ':');
+	interface = get_word (&argument, ':');
+
+	family = guess_ip_address_family (net);
+	connection = get_conn (connections, interface, NULL);
+
+	if (net && *net) {
+		if (!nm_utils_parse_inaddr_prefix_bin (family, net, &net_addr, &net_prefix)) {
+			_LOGW (LOGD_CORE, "Unrecognized address: %s\n", net);
+			return;
+		}
+	}
+
+	if (gateway && *net) {
+		if (!nm_utils_parse_inaddr_bin (family, gateway, &gateway_addr)) {
+			_LOGW (LOGD_CORE, "Unrecognized address: %s\n", gateway);
+			return;
+		}
+	}
+
+	switch (family) {
+	case AF_INET:
+		s_ip = nm_connection_get_setting_ip4_config (connection);
+		if (net_prefix == -1)
+			net_prefix = 32;
+		break;
+	case AF_INET6:
+		s_ip = nm_connection_get_setting_ip6_config (connection);
+		if (net_prefix == -1)
+			net_prefix = 128;
+		break;
+	default:
+		_LOGW (LOGD_CORE, "Unknown address family: %s\n", net);
+		return;
+	}
+
+	route = nm_ip_route_new_binary (family, &net_addr.addr_ptr, net_prefix, &gateway_addr.addr_ptr, -1, &error);
+	if (!route) {
+		g_warning ("Invalid route '%s via %s': %s\n", net, gateway, error->message);
+		g_clear_error (&error);
+		return;
+	}
+
+	nm_setting_ip_config_add_route (s_ip, route);
+	nm_ip_route_unref (route);
+}
+
+static void
+parse_vlan (GHashTable *connections, char *argument)
+{
+	NMConnection *connection;
+	NMSettingVlan *s_vlan;
+	const char *vlan;
+	const char *phy;
+	const char *vlanid;
+
+	vlan = get_word (&argument, ':');
+	phy = get_word (&argument, ':');
+
+	for (vlanid = vlan + strlen (vlan); vlanid > vlan; vlanid--) {
+		if (!g_ascii_isdigit (*(vlanid - 1)))
+			break;
+	}
+
+	connection = get_conn (connections, vlan, NM_SETTING_VLAN_SETTING_NAME);
+
+	s_vlan = nm_connection_get_setting_vlan (connection);
+	g_object_set (s_vlan,
+	              NM_SETTING_VLAN_PARENT, phy,
+	              NM_SETTING_VLAN_ID, g_ascii_strtoull (vlanid, NULL, 10),
+	              NULL);
+
+	if (argument && *argument)
+		_LOGW (LOGD_CORE, "Ignoring extra: '%s'.\n", argument);
+}
+
+static void
+parse_bootdev (GHashTable *connections, char *argument)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+
+	connection = get_conn (connections, NULL, NULL);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, argument,
+	              NULL);
+}
+
+static void
+parse_nameserver (GHashTable *connections, char *argument)
+{
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip = NULL;
+	char *dns;
+
+	connection = get_conn (connections, NULL, NULL);
+
+	dns = get_word (&argument, '\0');
+
+	switch (guess_ip_address_family (dns)) {
+	case AF_INET:
+		s_ip = nm_connection_get_setting_ip4_config (connection);
+		break;
+	case AF_INET6:
+		s_ip = nm_connection_get_setting_ip6_config (connection);
+		break;
+	default:
+		_LOGW (LOGD_CORE, "Unknown address family: %s\n", dns);
+		break;
+	}
+
+	nm_setting_ip_config_add_dns (s_ip, dns);
+
+	if (argument && *argument)
+		_LOGW (LOGD_CORE, "xIgnoring extra: '%s'.\n", argument);
+}
+
+static void
+parse_rd_peerdns (GHashTable *connections, char *argument)
+{
+	gboolean auto_dns = !_nm_utils_ascii_str_to_bool (argument, TRUE);
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip = NULL;
+
+	connection = get_conn (connections, NULL, NULL);
+
+	s_ip = nm_connection_get_setting_ip4_config (connection);
+	g_object_set (s_ip,
+	              NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, auto_dns,
+	              NULL);
+
+
+	s_ip = nm_connection_get_setting_ip6_config (connection);
+	g_object_set (s_ip,
+	              NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, auto_dns,
+	              NULL);
+}
+
+static void
+_normalize_conn (gpointer key, gpointer value, gpointer user_data)
+{
+	NMConnection *connection = value;
+
+	nm_connection_normalize (connection, NULL, NULL, NULL);
+}
+
+GHashTable *
+nmi_cmdline_reader_parse (const char *sysfs_dir, char **argv)
+{
+	GHashTable *connections;
+	const char *tag;
+	char *argument;
+	gboolean ignore_bootif = FALSE;
+	char *bootif = NULL;
+	int i;
+
+	connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+
+	for (i = 0; argv[i]; i++) {
+		argument = argv[i];
+		tag = get_word (&argument, '=');
+		if (strcmp (tag, "ip") == 0)
+			parse_ip (connections, sysfs_dir, argument);
+		else if (strcmp (tag, "rd.route") == 0)
+			parse_rd_route (connections, argument);
+		else if (strcmp (tag, "bridge") == 0)
+			parse_master (connections, argument, NM_SETTING_BRIDGE_SETTING_NAME);
+		else if (strcmp (tag, "bond") == 0)
+			parse_master (connections, argument, NM_SETTING_BOND_SETTING_NAME);
+		else if (strcmp (tag, "team") == 0)
+			parse_master (connections, argument, NM_SETTING_TEAM_SETTING_NAME);
+		else if (strcmp (tag, "vlan") == 0)
+			parse_vlan (connections, argument);
+		else if (strcmp (tag, "bootdev") == 0)
+			parse_bootdev (connections, argument);
+		else if (strcmp (tag, "nameserver") == 0)
+			parse_nameserver (connections, argument);
+		else if (strcmp (tag, "rd.peerdns") == 0)
+			parse_rd_peerdns (connections, argument);
+		else if (strcmp (tag, "rd.bootif") == 0)
+			ignore_bootif = !_nm_utils_ascii_str_to_bool (argument, TRUE);
+		else if (strcasecmp (tag, "BOOTIF") == 0)
+			bootif = argument;
+	}
+
+	if (ignore_bootif)
+		bootif = NULL;
+	if (bootif) {
+		NMConnection *connection;
+		NMSettingWired *s_wired;
+
+		connection = get_conn (connections, NULL, NM_SETTING_WIRED_SETTING_NAME);
+
+		s_wired = nm_connection_get_setting_wired (connection);
+		g_object_set (s_wired,
+		              NM_SETTING_WIRED_MAC_ADDRESS, bootif,
+		              NULL);
+	}
+
+	g_hash_table_foreach (connections, _normalize_conn, NULL);
+
+	return connections;
+}
diff --git a/src/initrd/nmi-ibft-reader.c b/src/initrd/nmi-ibft-reader.c
new file mode 100644
index 00000000..c9275467
--- /dev/null
+++ b/src/initrd/nmi-ibft-reader.c
@@ -0,0 +1,441 @@
+/* NetworkManager initrd configuration generator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 - 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-initrd-generator.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+#include <sys/inotify.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "nm-core-internal.h"
+#include "platform/nm-platform.h"
+#include "NetworkManagerUtils.h"
+
+/*****************************************************************************/
+
+#define _NMLOG(level, domain, ...) \
+    nm_log ((level), (domain), NULL, NULL, \
+            "ibft-reader: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__) \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+static GHashTable *
+load_one_nic (const char *sysfs_dir, const char *dir_name)
+{
+	gs_free char *nic_path = g_build_filename (sysfs_dir, dir_name, NULL);
+	GDir *nic_dir;
+	const char *entry_name;
+	char *content;
+	gs_free_error GError *error = NULL;
+	GHashTable *nic;
+
+	g_return_val_if_fail (sysfs_dir != NULL, FALSE);
+
+	nic_dir = g_dir_open (nic_path, 0, &error);
+	if (!nic_dir) {
+		_LOGW (LOGD_CORE, "Can't open %s: %s", nic_path, error->message);
+		return NULL;
+	}
+
+	nic = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+	while ((entry_name = g_dir_read_name (nic_dir))) {
+		gs_free char *entry_path = g_build_filename (nic_path, entry_name, NULL);
+
+		if (!g_file_test (entry_path, G_FILE_TEST_IS_REGULAR))
+			continue;
+
+		if (!g_file_get_contents (entry_path, &content, NULL, &error)) {
+			_LOGW (LOGD_CORE, "Can't read %s: %s", entry_path, error->message);
+			g_clear_error (&error);
+			continue;
+		}
+
+		g_strchomp (content);
+		if (!g_hash_table_insert (nic, g_strdup (entry_name), content))
+			_LOGW (LOGD_CORE, "Duplicate iBFT entry: %s", entry_name);
+	}
+
+	g_dir_close (nic_dir);
+
+	return nic;
+}
+
+GHashTable *
+nmi_ibft_read (const char *sysfs_dir)
+{
+	gs_free char *ibft_path = NULL;
+	GDir *ibft_dir;
+	const char *dir_name;
+	GHashTable *ibft, *nic;
+	char *mac;
+	gs_free_error GError *error = NULL;
+
+	g_return_val_if_fail (sysfs_dir != NULL, FALSE);
+
+	ibft_path = g_build_filename (sysfs_dir, "firmware", "ibft", NULL);
+
+	ibft = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free,
+	                              (GDestroyNotify) g_hash_table_unref);
+
+	if (!g_file_test (ibft_path, G_FILE_TEST_IS_DIR))
+		nm_utils_modprobe (NULL, FALSE, "iscsi_ibft", NULL);
+	if (!g_file_test (ibft_path, G_FILE_TEST_IS_DIR))
+		return ibft;
+
+	ibft_dir = g_dir_open (ibft_path, 0, &error);
+	if (!ibft_dir) {
+		_LOGW (LOGD_CORE, "Unable to open iBFT firmware directory: %s", error->message);
+		return ibft;
+	}
+
+	while ((dir_name = g_dir_read_name (ibft_dir))) {
+		if (!g_str_has_prefix (dir_name, "ethernet"))
+			continue;
+
+		nic = load_one_nic (ibft_path, dir_name);
+		mac = g_hash_table_lookup (nic, "mac");
+
+		if (!mac) {
+			_LOGW (LOGD_CORE, "Ignoring an iBFT record without a MAC address");
+			g_hash_table_unref (nic);
+			continue;
+		}
+
+		mac = g_ascii_strup (mac, -1);
+		if (!g_hash_table_insert (ibft, mac, nic))
+			_LOGW (LOGD_CORE, "Duplicate iBFT record for %s", mac);
+	}
+
+	g_dir_close (ibft_dir);
+
+	return ibft;
+}
+
+static gboolean
+ip_setting_add_from_block (GHashTable *nic,
+                            NMConnection *connection,
+                            GError **error)
+{
+	NMSettingIPConfig *s_ip = NULL;
+	NMSettingIPConfig *s_ip4 = NULL;
+	NMSettingIPConfig *s_ip6 = NULL;
+	NMIPAddress *addr;
+	const char *s_ipaddr = NULL;
+	const char *s_prefix = NULL;
+	const char *s_gateway = NULL;
+	const char *s_dns1 = NULL;
+	const char *s_dns2 = NULL;
+	const char *s_origin = NULL;
+	const char *method = NULL;
+	int family;
+	gint64 prefix;
+
+	s_ipaddr = (const char *)g_hash_table_lookup (nic, "ip-addr");
+	s_prefix = (const char *)g_hash_table_lookup (nic, "prefix-len");
+	s_gateway = (const char *)g_hash_table_lookup (nic, "gateway");
+	s_dns1 = (const char *)g_hash_table_lookup (nic, "primary-dns");
+	s_dns2 = (const char *)g_hash_table_lookup (nic, "secondary-dns");
+	s_origin = (const char *)g_hash_table_lookup (nic, "origin");
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (!s_ip4) {
+		s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_ip4);
+	}
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	if (!s_ip6) {
+		s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_ip6);
+	}
+
+	family = guess_ip_address_family (s_ipaddr);
+	if (family == AF_UNSPEC)
+		family = guess_ip_address_family (s_gateway);
+
+	switch (family) {
+	case AF_INET:
+		s_ip = s_ip4;
+		g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD,
+		              NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+		break;
+	case AF_INET6:
+		s_ip = s_ip6;
+		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD,
+		              NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL);
+		break;
+	default:
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "iBFT: invalid IP address '%s'.",
+		             s_ipaddr);
+		return FALSE;
+	}
+
+	if (   (g_strcmp0 (s_origin, "3") == 0 && family == AF_INET)
+	    || (g_strcmp0 (s_origin, "4") == 0 && family == AF_INET)) {
+		method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+	} else if (g_strcmp0 (s_origin, "3") == 0 && family == AF_INET6) {
+		method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
+	} else if (g_strcmp0 (s_origin, "4") == 0 && family == AF_INET6) {
+		method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+	} else if (family == AF_INET) {
+		method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
+	} else if (family == AF_INET6) {
+		method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+	} else {
+		g_return_val_if_reached (FALSE);
+	}
+	g_object_set (s_ip,
+                      NM_SETTING_IP_CONFIG_METHOD, method,
+                      NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
+	              NULL);
+
+	if (s_gateway && !nm_utils_ipaddr_valid (family, s_gateway)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "iBFT: invalid IP gateway '%s'.", s_gateway);
+		return FALSE;
+	}
+
+	if (s_dns1 && !nm_utils_ipaddr_valid (family, s_dns1)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "iBFT: invalid DNS1 address '%s'.", s_dns1);
+		return FALSE;
+	}
+
+	if (s_dns2 && !nm_utils_ipaddr_valid (family, s_dns2)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "iBFT: invalid DNS2 address '%s'.", s_dns2);
+		return FALSE;
+	}
+
+	if (s_ipaddr) {
+		prefix = _nm_utils_ascii_str_to_int64 (s_prefix, 10, 0, 128, -1);
+		if (prefix == -1) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "iBFT: invalid IP prefix '%s'.", s_prefix);
+			return FALSE;
+		}
+
+		addr = nm_ip_address_new (family, s_ipaddr, prefix, error);
+		if (!addr) {
+			g_prefix_error (error, "iBFT: ");
+			return FALSE;
+		}
+
+		nm_setting_ip_config_add_address (s_ip, addr);
+		nm_ip_address_unref (addr);
+
+		g_object_set (s_ip, NM_SETTING_IP_CONFIG_GATEWAY, s_gateway, NULL);
+	}
+
+	if (s_dns1)
+		nm_setting_ip_config_add_dns (s_ip, s_dns1);
+	if (s_dns2)
+		nm_setting_ip_config_add_dns (s_ip, s_dns2);
+
+	return TRUE;
+}
+
+static gboolean
+connection_setting_add (GHashTable *nic,
+                        NMConnection *connection,
+                        const char *type,
+                        const char *prefix,
+                        GError **error)
+{
+	NMSetting *s_con;
+	char *id, *uuid;
+	const char *s_index, *s_hwaddr, *s_ipaddr, *s_vlanid;
+
+	s_index = (const char *)g_hash_table_lookup (nic, "index");
+	s_hwaddr = (const char *)g_hash_table_lookup (nic, "mac");
+	s_ipaddr = (const char *)g_hash_table_lookup (nic, "ip-addr");
+	s_vlanid = (const char *)g_hash_table_lookup (nic, "vlan");
+
+	if (!s_hwaddr) {
+		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		                     "iBFT: missing MAC address");
+		return FALSE;
+	}
+
+	id = g_strdup_printf ("iBFT%s%s Connection%s%s",
+	                      prefix ? " " : "",
+	                      prefix ? prefix : "",
+	                      s_index ? " " : "",
+	                      s_index ? s_index : "");
+
+	uuid = _nm_utils_uuid_generate_from_strings ("ibft",
+	                                             s_hwaddr,
+	                                             s_vlanid ? "V" : "v",
+	                                             s_vlanid ? s_vlanid : "",
+	                                             s_ipaddr ? "A" : "DHCP",
+	                                             s_ipaddr ? s_ipaddr : "",
+	                                             NULL);
+
+	s_con = (NMSetting *) nm_connection_get_setting_connection (connection);
+	if (!s_con) {
+		s_con = nm_setting_connection_new ();
+		nm_connection_add_setting (connection, s_con);
+	}
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_TYPE, type,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NULL);
+
+	g_free (uuid);
+	g_free (id);
+
+	return TRUE;
+}
+
+static gboolean
+is_ibft_vlan_device (GHashTable *nic)
+{
+	const char *s_vlan_id;
+
+	g_assert (nic);
+
+	s_vlan_id = (const char *)g_hash_table_lookup (nic, "vlan");
+
+	if (s_vlan_id) {
+		/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it
+		 * means "no VLAN".
+		 */
+		if (_nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+vlan_setting_add_from_block (GHashTable *nic,
+                             NMConnection *connection,
+                             GError **error)
+{
+	NMSetting *s_vlan = NULL;
+	const char *vlan_id_str = NULL;
+	gint64 vlan_id = -1;
+
+	g_assert (nic);
+	g_assert (connection);
+
+	/* This won't fail since this function shouldn't be called unless the
+	 * iBFT VLAN ID exists and is > 0.
+	 */
+	vlan_id_str = (const char *)g_hash_table_lookup (nic, "vlan");
+	g_assert (vlan_id_str);
+
+	/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it means "no VLAN" */
+	vlan_id = _nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1);
+	if (vlan_id == -1) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "Invalid VLAN_ID '%s'", vlan_id_str);
+		return FALSE;
+	}
+
+	s_vlan = (NMSetting *) nm_connection_get_setting_vlan (connection);
+	if (!s_vlan) {
+		s_vlan = nm_setting_vlan_new ();
+		nm_connection_add_setting (connection, s_vlan);
+	}
+
+	g_object_set (s_vlan, NM_SETTING_VLAN_ID, (guint32) vlan_id, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+wired_setting_add_from_block (GHashTable *nic,
+                              NMConnection *connection,
+                              GError **error)
+{
+	NMSetting *s_wired = NULL;
+	const char *hwaddr = NULL;
+
+	g_assert (nic);
+	g_assert (connection);
+
+
+	hwaddr = (const char *)g_hash_table_lookup (nic, "mac");
+	if (!hwaddr) {
+		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		                     "iBFT: missing MAC address");
+		return FALSE;
+	}
+
+	if (!nm_utils_hwaddr_valid (hwaddr, ETH_ALEN)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "iBFT: invalid MAC address '%s'.", hwaddr);
+		return FALSE;
+	}
+
+	s_wired = (NMSetting *) nm_connection_get_setting_wired (connection);
+	if (!s_wired) {
+		s_wired = nm_setting_wired_new ();
+		nm_connection_add_setting (connection, s_wired);
+	}
+
+	g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, hwaddr, NULL);
+
+	return TRUE;
+}
+
+gboolean
+nmi_ibft_update_connection_from_nic (NMConnection *connection, GHashTable *nic, GError **error)
+{
+	gboolean is_vlan = FALSE;
+
+	g_assert (nic);
+
+	is_vlan = is_ibft_vlan_device (nic);
+	if (is_vlan && !vlan_setting_add_from_block (nic, connection, error))
+		return FALSE;
+
+	/* Always have a wired setting; for VLAN it defines the parent */
+	if (!wired_setting_add_from_block (nic, connection, error))
+		return FALSE;
+
+	if (!ip_setting_add_from_block (nic, connection, error))
+		return FALSE;
+
+	if (!connection_setting_add (nic,
+	                             connection,
+	                             is_vlan ? NM_SETTING_VLAN_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME,
+	                             is_vlan ? "VLAN" : NULL,
+	                             error))
+		return FALSE;
+
+	if (!nm_connection_normalize (connection, NULL, NULL, error))
+		return FALSE;
+
+	return TRUE;
+}
diff --git a/src/initrd/tests/sysfs-bad-dns1/class/net/eth0/address b/src/initrd/tests/sysfs-bad-dns1/class/net/eth0/address
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/class/net/eth0/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway
new file mode 100644
index 00000000..bf6cf454
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway
@@ -0,0 +1 @@
+192.168.35.254
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr
new file mode 100644
index 00000000..944c8d45
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr
@@ -0,0 +1 @@
+192.168.32.72
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len
new file mode 100644
index 00000000..8fdd954d
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len
@@ -0,0 +1 @@
+22
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns
new file mode 100644
index 00000000..2303dbdc
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns
@@ -0,0 +1 @@
+10000.500.250.1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns
new file mode 100644
index 00000000..2eb9e1d0
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns
@@ -0,0 +1 @@
+10.16.255.3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask
new file mode 100644
index 00000000..7cb9d0a9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask
@@ -0,0 +1 @@
+255.255.252.0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..509a7ab8
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.pjones6
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name
new file mode 100644
index 00000000..32c9450a
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name
@@ -0,0 +1 @@
+iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/class/net/eth0/address b/src/initrd/tests/sysfs-bad-dns2/class/net/eth0/address
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/class/net/eth0/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway
new file mode 100644
index 00000000..bf6cf454
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway
@@ -0,0 +1 @@
+192.168.35.254
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr
new file mode 100644
index 00000000..944c8d45
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr
@@ -0,0 +1 @@
+192.168.32.72
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len
new file mode 100644
index 00000000..8fdd954d
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len
@@ -0,0 +1 @@
+22
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns
new file mode 100644
index 00000000..2e152cb0
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns
@@ -0,0 +1 @@
+10.16.255.2
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns
new file mode 100644
index 00000000..5ed7b719
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns
@@ -0,0 +1 @@
+blah.foo.bar.baz
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask
new file mode 100644
index 00000000..7cb9d0a9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask
@@ -0,0 +1 @@
+255.255.252.0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..509a7ab8
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.pjones6
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name
new file mode 100644
index 00000000..32c9450a
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name
@@ -0,0 +1 @@
+iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/class/net/eth0/address b/src/initrd/tests/sysfs-bad-gateway/class/net/eth0/address
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/class/net/eth0/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway
new file mode 100644
index 00000000..1970856e
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway
@@ -0,0 +1 @@
+bb.cc.dd.ee
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr
new file mode 100644
index 00000000..944c8d45
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr
@@ -0,0 +1 @@
+192.168.32.72
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len
new file mode 100644
index 00000000..8fdd954d
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len
@@ -0,0 +1 @@
+22
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns
new file mode 100644
index 00000000..2e152cb0
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns
@@ -0,0 +1 @@
+10.16.255.2
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns
new file mode 100644
index 00000000..2eb9e1d0
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns
@@ -0,0 +1 @@
+10.16.255.3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask
new file mode 100644
index 00000000..7cb9d0a9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask
@@ -0,0 +1 @@
+255.255.252.0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..509a7ab8
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.pjones6
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name
new file mode 100644
index 00000000..32c9450a
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name
@@ -0,0 +1 @@
+iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address b/src/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway
new file mode 100644
index 00000000..bf6cf454
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway
@@ -0,0 +1 @@
+192.168.35.254
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr
new file mode 100644
index 00000000..d526f324
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr
@@ -0,0 +1 @@
+aa.bb.cc.dd
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len
new file mode 100644
index 00000000..8fdd954d
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len
@@ -0,0 +1 @@
+22
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns
new file mode 100644
index 00000000..2e152cb0
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns
@@ -0,0 +1 @@
+10.16.255.2
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns
new file mode 100644
index 00000000..2eb9e1d0
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns
@@ -0,0 +1 @@
+10.16.255.3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask
new file mode 100644
index 00000000..7cb9d0a9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask
@@ -0,0 +1 @@
+255.255.252.0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..509a7ab8
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.pjones6
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name
new file mode 100644
index 00000000..32c9450a
--- /dev/null
+++ b/src/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name
@@ -0,0 +1 @@
+iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/class/net/eth0/address b/src/initrd/tests/sysfs-dhcp/class/net/eth0/address
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/class/net/eth0/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/class/net/eth1/address b/src/initrd/tests/sysfs-dhcp/class/net/eth1/address
new file mode 100644
index 00000000..befa7850
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/class/net/eth1/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway
new file mode 100644
index 00000000..59ea5b82
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway
@@ -0,0 +1 @@
+10.16.52.254
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..e440e5c8
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns
new file mode 100644
index 00000000..2e152cb0
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns
@@ -0,0 +1 @@
+10.16.255.2
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns
new file mode 100644
index 00000000..2eb9e1d0
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns
@@ -0,0 +1 @@
+10.16.255.3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway
new file mode 100644
index 00000000..59ea5b82
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway
@@ -0,0 +1 @@
+10.16.52.254
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac
new file mode 100644
index 00000000..befa7850
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin
new file mode 100644
index 00000000..e440e5c8
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns
new file mode 100644
index 00000000..2e152cb0
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns
@@ -0,0 +1 @@
+10.16.255.2
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns
new file mode 100644
index 00000000..2eb9e1d0
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns
@@ -0,0 +1 @@
+10.16.255.3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..509a7ab8
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.pjones6
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name
new file mode 100644
index 00000000..32c9450a
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name
@@ -0,0 +1 @@
+iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name
new file mode 100644
index 00000000..ac3a6a2a
--- /dev/null
+++ b/src/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name
@@ -0,0 +1 @@
+iqn.1.2008-11.com.blahblah:iscsi1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/class/net/eth0/address b/src/initrd/tests/sysfs-static/class/net/eth0/address
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/class/net/eth0/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/class/net/eth1/address b/src/initrd/tests/sysfs-static/class/net/eth1/address
new file mode 100644
index 00000000..befa7850
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/class/net/eth1/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway
new file mode 100644
index 00000000..bf6cf454
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway
@@ -0,0 +1 @@
+192.168.35.254
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr
new file mode 100644
index 00000000..944c8d45
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr
@@ -0,0 +1 @@
+192.168.32.72
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len
new file mode 100644
index 00000000..8fdd954d
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len
@@ -0,0 +1 @@
+22
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns
new file mode 100644
index 00000000..2e152cb0
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns
@@ -0,0 +1 @@
+10.16.255.2
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns
new file mode 100644
index 00000000..2eb9e1d0
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns
@@ -0,0 +1 @@
+10.16.255.3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask
new file mode 100644
index 00000000..7cb9d0a9
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask
@@ -0,0 +1 @@
+255.255.252.0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway
new file mode 100644
index 00000000..59ea5b82
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway
@@ -0,0 +1 @@
+10.16.52.254
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac
new file mode 100644
index 00000000..befa7850
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin
new file mode 100644
index 00000000..e440e5c8
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns
new file mode 100644
index 00000000..2e152cb0
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns
@@ -0,0 +1 @@
+10.16.255.2
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns
new file mode 100644
index 00000000..2eb9e1d0
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns
@@ -0,0 +1 @@
+10.16.255.3
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..509a7ab8
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.pjones6
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target0/index b/src/initrd/tests/sysfs-static/firmware/ibft/target0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target0/lun b/src/initrd/tests/sysfs-static/firmware/ibft/target0/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target0/port b/src/initrd/tests/sysfs-static/firmware/ibft/target0/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target0/target-name b/src/initrd/tests/sysfs-static/firmware/ibft/target0/target-name
new file mode 100644
index 00000000..32c9450a
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target0/target-name
@@ -0,0 +1 @@
+iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target1/index b/src/initrd/tests/sysfs-static/firmware/ibft/target1/index
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target1/index
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr b/src/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr
new file mode 100644
index 00000000..7450fdf9
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr
@@ -0,0 +1 @@
+10.16.52.16
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target1/lun b/src/initrd/tests/sysfs-static/firmware/ibft/target1/lun
new file mode 100644
index 00000000..c43d0930
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target1/lun
@@ -0,0 +1 @@
+00000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target1/port b/src/initrd/tests/sysfs-static/firmware/ibft/target1/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target1/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-static/firmware/ibft/target1/target-name b/src/initrd/tests/sysfs-static/firmware/ibft/target1/target-name
new file mode 100644
index 00000000..ac3a6a2a
--- /dev/null
+++ b/src/initrd/tests/sysfs-static/firmware/ibft/target1/target-name
@@ -0,0 +1 @@
+iqn.1.2008-11.com.blahblah:iscsi1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/class/net/eth0/address b/src/initrd/tests/sysfs-vlan/class/net/eth0/address
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/class/net/eth0/address
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr
new file mode 100644
index 00000000..4923230c
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr
@@ -0,0 +1 @@
+192.168.6.200
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..20659db7
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..56a6051c
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len
new file mode 100644
index 00000000..cabf43b5
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len
@@ -0,0 +1 @@
+24
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask
new file mode 100644
index 00000000..5a279b41
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask
@@ -0,0 +1 @@
+255.255.255.0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..d800886d
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+123
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..8e9cd2c4
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/index b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/index
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..a32ce01b
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+192.168.6.32
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun
new file mode 100644
index 00000000..f309ca6a
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+01000000
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/port b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/port
new file mode 100644
index 00000000..58f0d919
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name
new file mode 100644
index 00000000..f0e61157
--- /dev/null
+++ b/src/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name
@@ -0,0 +1 @@
+iqn.2003-01.org.x:disk1
\ No newline at end of file
diff --git a/src/initrd/tests/sysfs/class/net/eth0/address b/src/initrd/tests/sysfs/class/net/eth0/address
new file mode 100644
index 00000000..1f2f610e
--- /dev/null
+++ b/src/initrd/tests/sysfs/class/net/eth0/address
@@ -0,0 +1 @@
+00:53:00:AB:00:01
diff --git a/src/initrd/tests/sysfs/class/net/eth2/address b/src/initrd/tests/sysfs/class/net/eth2/address
new file mode 100644
index 00000000..6ec1078a
--- /dev/null
+++ b/src/initrd/tests/sysfs/class/net/eth2/address
@@ -0,0 +1 @@
+00:53:06:66:ab:01
diff --git a/src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id b/src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id
new file mode 100644
index 00000000..590d9cd2
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id
@@ -0,0 +1 @@
+NMTst
diff --git a/src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id b/src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id
new file mode 100644
index 00000000..35d4d09c
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id
@@ -0,0 +1 @@
+Whatevs
diff --git a/src/initrd/tests/sysfs/firmware/ibft/acpi_header/signature b/src/initrd/tests/sysfs/firmware/ibft/acpi_header/signature
new file mode 100644
index 00000000..e3a5d7ef
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/acpi_header/signature
@@ -0,0 +1 @@
+IBFT
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp
new file mode 100644
index 00000000..98a0d797
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:0002
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/flags b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/flags
new file mode 100644
index 00000000..00750edc
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/flags
@@ -0,0 +1 @@
+3
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway
new file mode 100644
index 00000000..7f378f8f
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:0001
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname
new file mode 100644
index 00000000..3ed5163c
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname
@@ -0,0 +1 @@
+host0.example.com
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/index b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/index
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/index
@@ -0,0 +1 @@
+0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr
new file mode 100644
index 00000000..98a0d797
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:0002
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/mac b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/mac
new file mode 100644
index 00000000..f2441319
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/mac
@@ -0,0 +1 @@
+00:53:00:ab:00:01
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/origin b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/origin
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/origin
@@ -0,0 +1 @@
+4
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len
new file mode 100644
index 00000000..900731ff
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len
@@ -0,0 +1 @@
+64
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns
new file mode 100644
index 00000000..e7511d5a
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:0053
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns
new file mode 100644
index 00000000..1f6c5c95
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:5353
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask
new file mode 100644
index 00000000..5df93454
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask
@@ -0,0 +1 @@
+255.255.255.255
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan
new file mode 100644
index 00000000..7cc86ad1
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan
@@ -0,0 +1 @@
+666
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp
new file mode 100644
index 00000000..61ef5585
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp
@@ -0,0 +1 @@
+192.0.2.2
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/flags b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/flags
new file mode 100644
index 00000000..00750edc
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/flags
@@ -0,0 +1 @@
+3
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway
new file mode 100644
index 00000000..2096f194
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway
@@ -0,0 +1 @@
+192.0.2.1
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname
new file mode 100644
index 00000000..5dc539d5
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname
@@ -0,0 +1 @@
+host2.example.com
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/index b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/index
new file mode 100644
index 00000000..0cfbf088
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/index
@@ -0,0 +1 @@
+2
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr
new file mode 100644
index 00000000..61ef5585
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr
@@ -0,0 +1 @@
+192.0.2.2
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/mac b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/mac
new file mode 100644
index 00000000..2fc3d525
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/mac
@@ -0,0 +1 @@
+00:53:06:66:AB:01
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/origin b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/origin
new file mode 100644
index 00000000..00750edc
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/origin
@@ -0,0 +1 @@
+3
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len
new file mode 100644
index 00000000..a45fd52c
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len
@@ -0,0 +1 @@
+24
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns
new file mode 100644
index 00000000..6fa8c742
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns
@@ -0,0 +1 @@
+192.0.2.53
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns
new file mode 100644
index 00000000..a7269ff5
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns
@@ -0,0 +1 @@
+192.0.2.54
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask
new file mode 100644
index 00000000..d30f9e9f
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask
@@ -0,0 +1 @@
+255.255.255.0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan
@@ -0,0 +1 @@
+0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/initiator/flags b/src/initrd/tests/sysfs/firmware/ibft/initiator/flags
new file mode 100644
index 00000000..00750edc
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/initiator/flags
@@ -0,0 +1 @@
+3
diff --git a/src/initrd/tests/sysfs/firmware/ibft/initiator/index b/src/initrd/tests/sysfs/firmware/ibft/initiator/index
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/initiator/index
@@ -0,0 +1 @@
+0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name b/src/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name
new file mode 100644
index 00000000..337b5a0f
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name
@@ -0,0 +1 @@
+iqn.1994-05.com.example.initiator:48b055856417
diff --git a/src/initrd/tests/sysfs/firmware/ibft/initiator/isns-server b/src/initrd/tests/sysfs/firmware/ibft/initiator/isns-server
new file mode 100644
index 00000000..4aff9398
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/initiator/isns-server
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:3205
diff --git a/src/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server b/src/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server
new file mode 100644
index 00000000..a26fb649
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:1812
diff --git a/src/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server b/src/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server
new file mode 100644
index 00000000..908be27f
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server
@@ -0,0 +1 @@
+192.0.2.181
diff --git a/src/initrd/tests/sysfs/firmware/ibft/initiator/slp-server b/src/initrd/tests/sysfs/firmware/ibft/initiator/slp-server
new file mode 100644
index 00000000..fca9e24b
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/initiator/slp-server
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:0427
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target0/chap-type b/src/initrd/tests/sysfs/firmware/ibft/target0/chap-type
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target0/chap-type
@@ -0,0 +1 @@
+0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target0/flags b/src/initrd/tests/sysfs/firmware/ibft/target0/flags
new file mode 100644
index 00000000..00750edc
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target0/flags
@@ -0,0 +1 @@
+3
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target0/index b/src/initrd/tests/sysfs/firmware/ibft/target0/index
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target0/index
@@ -0,0 +1 @@
+0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target0/ip-addr b/src/initrd/tests/sysfs/firmware/ibft/target0/ip-addr
new file mode 100644
index 00000000..994b6e9c
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target0/ip-addr
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0000:3260
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target0/lun b/src/initrd/tests/sysfs/firmware/ibft/target0/lun
new file mode 100644
index 00000000..7c295bd4
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target0/lun
@@ -0,0 +1 @@
+01000000
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc b/src/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc
@@ -0,0 +1 @@
+0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target0/port b/src/initrd/tests/sysfs/firmware/ibft/target0/port
new file mode 100644
index 00000000..53f0295f
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target0/port
@@ -0,0 +1 @@
+3260
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/chap-type b/src/initrd/tests/sysfs/firmware/ibft/target2/chap-type
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/chap-type
@@ -0,0 +1 @@
+0
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/flags b/src/initrd/tests/sysfs/firmware/ibft/target2/flags
new file mode 100644
index 00000000..00750edc
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/flags
@@ -0,0 +1 @@
+3
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/index b/src/initrd/tests/sysfs/firmware/ibft/target2/index
new file mode 100644
index 00000000..0cfbf088
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/index
@@ -0,0 +1 @@
+2
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/ip-addr b/src/initrd/tests/sysfs/firmware/ibft/target2/ip-addr
new file mode 100644
index 00000000..a210477e
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/ip-addr
@@ -0,0 +1 @@
+2001:0db8:0000:0000:0000:0000:0002:3260
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/lun b/src/initrd/tests/sysfs/firmware/ibft/target2/lun
new file mode 100644
index 00000000..7c295bd4
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/lun
@@ -0,0 +1 @@
+01000000
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc b/src/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc
new file mode 100644
index 00000000..0cfbf088
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc
@@ -0,0 +1 @@
+2
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/port b/src/initrd/tests/sysfs/firmware/ibft/target2/port
new file mode 100644
index 00000000..53f0295f
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/port
@@ -0,0 +1 @@
+3260
diff --git a/src/initrd/tests/sysfs/firmware/ibft/target2/target-name b/src/initrd/tests/sysfs/firmware/ibft/target2/target-name
new file mode 100644
index 00000000..f4094320
--- /dev/null
+++ b/src/initrd/tests/sysfs/firmware/ibft/target2/target-name
@@ -0,0 +1 @@
+iqn.1994-05.com.example.target:48b055851337
diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c
new file mode 100644
index 00000000..4db71147
--- /dev/null
+++ b/src/initrd/tests/test-cmdline-reader.c
@@ -0,0 +1,787 @@
+/* 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.
+ */
+
+#include "nm-default.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.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_auto (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=auto", NULL });
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+
+	connection = g_hash_table_lookup (connections, "default_connection");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+
+	g_assert (!nm_connection_get_setting_vlan (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), ==, "Wired 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_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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0);
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip4));
+
+	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_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 0);
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip6), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+}
+
+static void
+test_if_auto_with_mtu (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=eth0:auto:1666", NULL });
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1666);
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+
+	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_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+}
+
+
+static void
+test_if_dhcp6 (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=eth1:dhcp6", NULL });
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+	connection = g_hash_table_lookup (connections, "eth1");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth1");
+
+	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_DISABLED);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+}
+
+
+static void
+test_if_auto_with_mtu_and_mac (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=eth2:auto6:2048:00:53:ef:12:34:56", NULL });
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+
+	connection = g_hash_table_lookup (connections, "eth2");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth2");
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 2048);
+	g_assert_cmpstr (nm_setting_wired_get_cloned_mac_address (s_wired), ==, "00:53:EF:12:34:56");
+
+	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_DISABLED);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+
+	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_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+}
+
+
+static void
+test_if_ip4_manual (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){
+		"ip=192.0.2.2::192.0.2.1:255.255.255.0:"
+		"hostname0.example.com:eth3::192.0.2.53", NULL });
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip4;
+	NMIPAddress *ip_addr;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+
+	connection = g_hash_table_lookup (connections, "eth3");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth3");
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 1);
+	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "192.0.2.53");
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 0);
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1);
+	ip_addr = nm_setting_ip_config_get_address (s_ip4, 0);
+	g_assert (ip_addr);
+	g_assert_cmpstr (nm_ip_address_get_address (ip_addr), ==, "192.0.2.2");
+	g_assert_cmpint (nm_ip_address_get_prefix (ip_addr), ==, 24);
+	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.0.2.1");
+	g_assert_cmpstr (nm_setting_ip_config_get_dhcp_hostname (s_ip4), ==, "hostname0.example.com");
+}
+
+
+static void
+test_if_ip6_manual (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){
+		"ip=[2001:0db8::02]/64::[2001:0db8::01]::"
+		"hostname0.example.com:eth4::[2001:0db8::53]",
+		NULL
+	});
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip6;
+	NMIPAddress *ip_addr;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+
+	connection = g_hash_table_lookup (connections, "eth4");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth4");
+
+	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_MANUAL);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 1);
+	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip6, 0), ==, "2001:db8::53");
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 0);
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip6), ==, 1);
+	ip_addr = nm_setting_ip_config_get_address (s_ip6, 0);
+	g_assert (ip_addr);
+	g_assert_cmpstr (nm_ip_address_get_address (ip_addr), ==, "2001:db8::2");
+	g_assert_cmpint (nm_ip_address_get_prefix (ip_addr), ==, 64);
+	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip6), ==, "2001:db8::1");
+	g_assert_cmpstr (nm_setting_ip_config_get_dhcp_hostname (s_ip6), ==, "hostname0.example.com");
+}
+
+static void
+test_multiple (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=192.0.2.2:::::eth0", "ip=[2001:db8::2]:::::eth0", NULL });
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMIPAddress *ip_addr;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1);
+	ip_addr = nm_setting_ip_config_get_address (s_ip4, 0);
+	g_assert (ip_addr);
+	g_assert_cmpstr (nm_ip_address_get_address (ip_addr), ==, "192.0.2.2");
+
+	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_MANUAL);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip6), ==, 1);
+	ip_addr = nm_setting_ip_config_get_address (s_ip6, 0);
+	g_assert (ip_addr);
+	g_assert_cmpstr (nm_ip_address_get_address (ip_addr), ==, "2001:db8::2");
+}
+
+static void
+test_some_more (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "bootdev=eth1", "hail", "nameserver=[2001:DB8:3::53]",
+	                                                 "satan", "nameserver=192.0.2.53", "worship",
+	                                                 "BOOTIF=00-53-AB-cd-02-03", "doom", "rd.peerdns=0",
+	                                                 "rd.route=[2001:DB8:3::/48]:[2001:DB8:2::1]:ens10",
+	                                                 NULL });
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMIPRoute *ip_route;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 2);
+
+	connection = g_hash_table_lookup (connections, "default_connection");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (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), ==, "Wired Connection");
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eth1");
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	g_assert_cmpstr (nm_setting_wired_get_mac_address (s_wired), ==, "00:53:AB:CD:02:03");
+
+	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 (nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 1);
+	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "192.0.2.53");
+
+
+	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_assert (nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 1);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip6, 0), ==, "2001:db8:3::53");
+
+
+	connection = g_hash_table_lookup (connections, "ens10");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (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), ==, "ens10");
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "ens10");
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	g_assert (!nm_setting_wired_get_mac_address (s_wired));
+
+	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_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 0);
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 1);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+	ip_route = nm_setting_ip_config_get_route (s_ip6, 0);
+	g_assert_cmpstr (nm_ip_route_get_dest (ip_route), ==, "2001:db8:3::");
+	g_assert_cmpint (nm_ip_route_get_family (ip_route), ==, AF_INET6);
+	g_assert_cmpint (nm_ip_route_get_metric (ip_route), ==, -1);
+	g_assert_cmpstr (nm_ip_route_get_next_hop (ip_route), ==, "2001:db8:2::1");
+	g_assert_cmpint (nm_ip_route_get_prefix (ip_route), ==, 48);
+}
+
+static void
+test_no_bootif (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "BOOTIF=00-53-AB-cd-02-03", "rd.bootif=0", NULL });
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 0);
+}
+
+static void
+test_bond (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "rd.route=192.0.2.53::bong0", "bond=bong0:eth0,eth1:mode=balance-rr", NULL });
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMSettingBond *s_bond;
+	NMIPRoute *ip_route;
+	const char *master_uuid;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 3);
+
+	connection = g_hash_table_lookup (connections, "bong0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_connection_type (connection), ==, NM_SETTING_BOND_SETTING_NAME);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "bong0");
+	master_uuid = nm_connection_get_uuid (connection);
+	g_assert (master_uuid);
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 1);
+	ip_route = nm_setting_ip_config_get_route (s_ip4, 0);
+	g_assert_cmpstr (nm_ip_route_get_dest (ip_route), ==, "192.0.2.53");
+	g_assert_cmpint (nm_ip_route_get_family (ip_route), ==, AF_INET);
+	g_assert_cmpint (nm_ip_route_get_metric (ip_route), ==, -1);
+	g_assert (!nm_ip_route_get_next_hop (ip_route));
+	g_assert_cmpint (nm_ip_route_get_prefix (ip_route), ==, 32);
+
+	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_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 0);
+
+	s_bond = nm_connection_get_setting_bond (connection);
+	g_assert (s_bond);
+	g_assert_cmpint (nm_setting_bond_get_num_options (s_bond), ==, 1);
+	g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, "mode"), ==, "balance-rr");
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	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), ==, "eth0");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BOND_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+
+	connection = g_hash_table_lookup (connections, "eth1");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth1");
+
+	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), ==, "eth1");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BOND_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+}
+
+static void
+test_bond_default (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "bond", NULL });
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMSettingBond *s_bond;
+	const char *master_uuid;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 2);
+
+	connection = g_hash_table_lookup (connections, "bond0");
+
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_connection_type (connection), ==, NM_SETTING_BOND_SETTING_NAME);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "bond0");
+	master_uuid = nm_connection_get_uuid (connection);
+	g_assert (master_uuid);
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 0);
+
+	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_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 0);
+
+	s_bond = nm_connection_get_setting_bond (connection);
+	g_assert (s_bond);
+	g_assert_cmpint (nm_setting_bond_get_num_options (s_bond), ==, 1);
+	g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, "mode"), ==, "balance-rr");
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	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), ==, "eth0");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BOND_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+}
+
+static void
+test_bridge (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "bridge=bridge0:eth0,eth1", "rd.route=192.0.2.53::bridge0", NULL });
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMSettingBridge *s_bridge;
+	NMIPRoute *ip_route;
+	const char *master_uuid;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 3);
+
+	connection = g_hash_table_lookup (connections, "bridge0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_connection_type (connection), ==, NM_SETTING_BRIDGE_SETTING_NAME);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "bridge0");
+	master_uuid = nm_connection_get_uuid (connection);
+	g_assert (master_uuid);
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 1);
+	ip_route = nm_setting_ip_config_get_route (s_ip4, 0);
+	g_assert_cmpstr (nm_ip_route_get_dest (ip_route), ==, "192.0.2.53");
+	g_assert_cmpint (nm_ip_route_get_family (ip_route), ==, AF_INET);
+	g_assert_cmpint (nm_ip_route_get_metric (ip_route), ==, -1);
+	g_assert (!nm_ip_route_get_next_hop (ip_route));
+	g_assert_cmpint (nm_ip_route_get_prefix (ip_route), ==, 32);
+
+	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_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 0);
+
+	s_bridge = nm_connection_get_setting_bridge (connection);
+	g_assert (s_bridge);
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	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), ==, "eth0");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+
+	connection = g_hash_table_lookup (connections, "eth1");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth1");
+
+	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), ==, "eth1");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+}
+
+static void
+test_bridge_default (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "bridge", NULL });
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMSettingBridge *s_bridge;
+	const char *master_uuid;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 2);
+
+	connection = g_hash_table_lookup (connections, "bridge0");
+
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_connection_type (connection), ==, NM_SETTING_BRIDGE_SETTING_NAME);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "bridge0");
+	master_uuid = nm_connection_get_uuid (connection);
+	g_assert (master_uuid);
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 0);
+
+	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_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 0);
+
+	s_bridge = nm_connection_get_setting_bridge (connection);
+	g_assert (s_bridge);
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	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), ==, "eth0");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+}
+
+static void
+test_team (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "team=team0:eth0,eth1", "ip=team0:dhcp6", NULL });
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMSettingTeam *s_team;
+	const char *master_uuid;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 3);
+
+	connection = g_hash_table_lookup (connections, "team0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_connection_type (connection), ==, NM_SETTING_TEAM_SETTING_NAME);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "team0");
+	master_uuid = nm_connection_get_uuid (connection);
+	g_assert (master_uuid);
+
+	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_DISABLED);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip4));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 0);
+
+	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 (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 0);
+	g_assert (!nm_setting_ip_config_get_gateway (s_ip6));
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 0);
+
+	s_team = nm_connection_get_setting_team (connection);
+	g_assert (s_team);
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	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), ==, "eth0");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_TEAM_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+
+	connection = g_hash_table_lookup (connections, "eth1");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth1");
+
+	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), ==, "eth1");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_TEAM_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid);
+}
+
+static void
+test_ibft (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=ibft", NULL });
+	NMConnection *connection;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 2);
+
+	connection = g_hash_table_lookup (connections, "ibft0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "iBFT VLAN Connection 0");
+
+	connection = g_hash_table_lookup (connections, "ibft2");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "iBFT Connection 2");
+}
+
+static void
+test_ignore_extra (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	gs_strfreev char **argv = g_strdupv ((char *[]){ "blabla", "extra", "lalala", NULL });
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 0);
+}
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
+
+	g_test_add_func ("/initrd/cmdline/auto", test_auto);
+	g_test_add_func ("/initrd/cmdline/if_auto_with_mtu", test_if_auto_with_mtu);
+	g_test_add_func ("/initrd/cmdline/if_dhcp6", test_if_dhcp6);
+	g_test_add_func ("/initrd/cmdline/if_auto_with_mtu_and_mac", test_if_auto_with_mtu_and_mac);
+	g_test_add_func ("/initrd/cmdline/if_ip4_manual", test_if_ip4_manual);
+	g_test_add_func ("/initrd/cmdline/if_ip6_manual", test_if_ip6_manual);
+	g_test_add_func ("/initrd/cmdline/multiple", test_multiple);
+	g_test_add_func ("/initrd/cmdline/some_more", test_some_more);
+	g_test_add_func ("/initrd/cmdline/no_bootif", test_no_bootif);
+	g_test_add_func ("/initrd/cmdline/bond", test_bond);
+	g_test_add_func ("/initrd/cmdline/bond/default", test_bond_default);
+	g_test_add_func ("/initrd/cmdline/team", test_team);
+	g_test_add_func ("/initrd/cmdline/bridge", test_bridge);
+	g_test_add_func ("/initrd/cmdline/bridge/default", test_bridge_default);
+	g_test_add_func ("/initrd/cmdline/ibft", test_ibft);
+	g_test_add_func ("/initrd/cmdline/ignore_extra", test_ignore_extra);
+
+	return g_test_run ();
+}
diff --git a/src/initrd/tests/test-ibft-reader.c b/src/initrd/tests/test-ibft-reader.c
new file mode 100644
index 00000000..340b3896
--- /dev/null
+++ b/src/initrd/tests/test-ibft-reader.c
@@ -0,0 +1,286 @@
+/* NetworkManager initrd configuration generator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 - 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.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 NMConnection *
+read_connection (const char *sysfs_dir, const char *expected_mac, GError **error)
+{
+	NMConnection *connection = NULL;
+	gs_unref_hashtable GHashTable *ibft = NULL;
+	gs_free char *mac = NULL;
+	GHashTable *nic = NULL;
+
+	ibft = nmi_ibft_read (sysfs_dir);
+
+	mac = g_ascii_strup (expected_mac, -1);
+	nic = g_hash_table_lookup (ibft, mac);
+	if (!nic)
+		return NULL;
+
+	connection = nm_simple_connection_new ();
+
+	if (!nmi_ibft_update_connection_from_nic (connection, nic, error))
+		g_clear_object (&connection);
+
+	return connection;
+}
+
+static void
+test_read_ibft_dhcp (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	GError *error = NULL;
+	const char *mac_address;
+	const char *expected_mac_address = "00:33:21:98:b9:f1";
+
+	connection = read_connection (TEST_INITRD_DIR "/sysfs-dhcp", expected_mac_address, &error);
+	g_assert_no_error (error);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+
+	g_assert (!nm_connection_get_setting_vlan (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), ==, "iBFT Connection 1");
+	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, expected_mac_address, -1));
+	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);
+
+	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_IGNORE);
+
+	g_object_unref (connection);
+}
+
+static void
+test_read_ibft_static (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	GError *error = NULL;
+	const char *mac_address;
+	const char *expected_mac_address = "00:33:21:98:b9:f0";
+	NMIPAddress *ip4_addr;
+
+	connection = read_connection (TEST_INITRD_DIR "/sysfs-static", expected_mac_address, &error);
+	g_assert_no_error (error);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+
+	g_assert (!nm_connection_get_setting_vlan (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), ==, "iBFT Connection 0");
+	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, expected_mac_address, -1));
+	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_dns (s_ip4), ==, 2);
+	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "10.16.255.2");
+	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "10.16.255.3");
+
+	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.72");
+	g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 22);
+
+	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.35.254");
+
+	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_IGNORE);
+
+	g_object_unref (connection);
+}
+
+
+static void
+test_read_ibft_bad_address (gconstpointer user_data)
+{
+	const char *sysfs_dir = user_data;
+	NMConnection *connection;
+	GError *error = NULL;
+
+	g_assert (g_file_test (sysfs_dir, G_FILE_TEST_EXISTS));
+
+	connection = read_connection (sysfs_dir, "00:33:21:98:b9:f0", &error);
+	g_assert (connection == NULL);
+	g_assert (error);
+	g_clear_error (&error);
+}
+
+static void
+test_read_ibft_vlan (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingVlan *s_vlan;
+	NMSettingIPConfig *s_ip4;
+	const char *mac_address;
+	const char *expected_mac_address = "00:33:21:98:b9:f0";
+	NMIPAddress *ip4_addr;
+	GError *error = NULL;
+
+	connection = read_connection (TEST_INITRD_DIR "/sysfs-vlan", expected_mac_address, &error);
+	g_assert_no_error (error);
+	nmtst_assert_connection_verifies_without_normalization (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_VLAN_SETTING_NAME);
+
+	/* ===== WIRED SETTING ===== */
+	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, expected_mac_address, -1));
+
+	/* ===== VLAN SETTING ===== */
+	s_vlan = nm_connection_get_setting_vlan (connection);
+	g_assert (s_vlan);
+	g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 123);
+	g_assert_cmpstr (nm_setting_vlan_get_parent (s_vlan), ==, NULL);
+
+	/* ===== IPv4 SETTING ===== */
+	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_dns (s_ip4), ==, 0);
+
+	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.6.200");
+	g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24);
+
+	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, NULL);
+
+	g_object_unref (connection);
+}
+
+static void
+test_read_ibft (void)
+{
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	GError *error = NULL;
+
+	/* This test doesn't actually test too much (apart from the presence of
+	 * IPv6 that is not covered by other tests), but the test fixture is a good
+	 * example of about everything that can be included in iBFT table (as of
+	 * ACPI 3.0b). */
+
+	connection = read_connection (TEST_INITRD_DIR "/sysfs", "00:53:00:AB:00:01", &error);
+	g_assert (connection);
+	g_assert_no_error (error);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert (nm_setting_ip_config_get_num_addresses (s_ip4) == 0);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (nm_setting_ip_config_get_num_addresses (s_ip6) == 1);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+	g_object_unref (connection);
+
+	connection = read_connection (TEST_INITRD_DIR "/sysfs", "00:53:06:66:AB:01", &error);
+	g_assert (connection);
+	g_assert_no_error (error);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	g_assert (nm_setting_ip_config_get_num_addresses (s_ip4) == 1);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (nm_setting_ip_config_get_num_addresses (s_ip6) == 0);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
+	g_object_unref (connection);
+}
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
+
+	g_test_add_func ("/initrd/ibft", test_read_ibft);
+	g_test_add_func ("/initrd/ibft/dhcp", test_read_ibft_dhcp);
+	g_test_add_func ("/initrd/ibft/static", test_read_ibft_static);
+	g_test_add_func ("/initrd/ibft/vlan", test_read_ibft_vlan);
+	g_test_add_data_func ("/initrd/ibft/bad-ipaddr-read", TEST_INITRD_DIR "/sysfs-bad-ipaddr", test_read_ibft_bad_address);
+	g_test_add_data_func ("/initrd/ibft/bad-gateway-read", TEST_INITRD_DIR "/sysfs-bad-gateway", test_read_ibft_bad_address);
+	g_test_add_data_func ("/initrd/ibft/bad-dns1-read", TEST_INITRD_DIR "/sysfs-bad-dns1", test_read_ibft_bad_address);
+	g_test_add_data_func ("/initrd/ibft/bad-dns2-read", TEST_INITRD_DIR "/sysfs-bad-dns2", test_read_ibft_bad_address);
+
+	return g_test_run ();
+}
diff --git a/src/main.c b/src/main.c
index a5bfb6af..f834fa94 100644
--- a/src/main.c
+++ b/src/main.c
@@ -397,8 +397,13 @@ main (int argc, char *argv[])
 	                                                         NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
 	                                                         NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL));
 
-	if (!nm_dbus_manager_acquire_bus (nm_dbus_manager_get ()))
-		goto done_no_manager;
+	if (!nm_config_get_configure_and_quit (config)) {
+		/* D-Bus is useless in configure and quit mode -- we're eventually dropping
+		 * off and potential clients would have no way of knowing whether we're
+		 * finished already or didn't start yet. */
+		if (!nm_dbus_manager_acquire_bus (nm_dbus_manager_get ()))
+			goto done_no_manager;
+	}
 
 	manager = nm_manager_setup ();
 	nm_dbus_manager_start (nm_dbus_manager_get(),
diff --git a/src/meson.build b/src/meson.build
index f46366a9..87c57671 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -245,6 +245,7 @@ if enable_ppp
 endif
 
 subdir('devices')
+subdir('initrd')
 subdir('settings/plugins')
 
 # NetworkManager binary
@@ -252,11 +253,27 @@ subdir('settings/plugins')
 create_exports_networkmanager = join_paths(meson.source_root(), 'tools', 'create-exports-NetworkManager.sh')
 symbol_map_name = 'NetworkManager.ver'
 
+# libNetworkManager.a, as built by meson doesn't contain all symbols
+# from libNetworkManagerBase.a and other static libraries, unless we
+# add dependencies with link_whole, only supported in meson >= 0.46.
+# Create an executable with full symbols that we use in place of the
+# library to enumerate the symbols.
+network_manager_sym = executable(
+  'nm-full-symbols',
+  'main.c',
+  c_args: nm_cflags,
+  link_args: '-Wl,--no-gc-sections',
+  dependencies: nm_deps,
+  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: [ libnetwork_manager, core_plugins ],
+  depends: [ network_manager_sym, core_plugins ],
   command: [create_exports_networkmanager, '--called-from-build', '@INPUT@']
 )
 
@@ -273,3 +290,12 @@ network_manager = executable(
   install: true,
   install_dir: nm_sbindir
 )
+
+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()])
+  endforeach
+endif
diff --git a/src/ndisc/nm-fake-ndisc.c b/src/ndisc/nm-fake-ndisc.c
index 15abee88..6e9a72c6 100644
--- a/src/ndisc/nm-fake-ndisc.c
+++ b/src/ndisc/nm-fake-ndisc.c
@@ -296,7 +296,7 @@ receive_ra (gpointer user_data)
 				.dad_counter = 0,
 			};
 
-			if (nm_ndisc_complete_and_add_address (ndisc, &address))
+			if (nm_ndisc_complete_and_add_address (ndisc, &address, now))
 				changed |= NM_NDISC_CONFIG_ADDRESSES;
 		}
 	}
diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c
index c0a0cd40..9f427d4d 100644
--- a/src/ndisc/nm-lndp-ndisc.c
+++ b/src/ndisc/nm-lndp-ndisc.c
@@ -221,10 +221,10 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 				.preferred = ndp_msg_opt_prefix_preferred_time (msg, offset),
 			};
 
-			if (address.preferred > address.lifetime)
-				address.preferred = address.lifetime;
-			if (nm_ndisc_complete_and_add_address (ndisc, &address))
-				changed |= NM_NDISC_CONFIG_ADDRESSES;
+			if (address.preferred <= address.lifetime) {
+				if (nm_ndisc_complete_and_add_address (ndisc, &address, now))
+					changed |= NM_NDISC_CONFIG_ADDRESSES;
+			}
 		}
 	}
 	ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_ROUTE) {
@@ -491,13 +491,17 @@ receive_rs (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 static gboolean
 event_ready (GIOChannel *source, GIOCondition condition, NMNDisc *ndisc)
 {
+	gs_unref_object NMNDisc *ndisc_keep_alive = g_object_ref (ndisc);
 	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMLndpNDiscPrivate *priv = NM_LNDP_NDISC_GET_PRIVATE ((NMLndpNDisc *) ndisc);
 
 	_LOGD ("processing libndp events");
 
-	if (!nm_ndisc_netns_push (ndisc, &netns))
-		return G_SOURCE_CONTINUE;
+	if (!nm_ndisc_netns_push (ndisc, &netns)) {
+		/* something is very wrong. Stop handling events. */
+		priv->event_id = 0;
+		return G_SOURCE_REMOVE;
+	}
 
 	ndp_callall_eventfd_handler (priv->ndp);
 	return G_SOURCE_CONTINUE;
diff --git a/src/ndisc/nm-ndisc-private.h b/src/ndisc/nm-ndisc-private.h
index bbecb01a..ae03c0ee 100644
--- a/src/ndisc/nm-ndisc-private.h
+++ b/src/ndisc/nm-ndisc-private.h
@@ -40,7 +40,7 @@ void nm_ndisc_ra_received (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed)
 void nm_ndisc_rs_received (NMNDisc *ndisc);
 
 gboolean nm_ndisc_add_gateway              (NMNDisc *ndisc, const NMNDiscGateway *new);
-gboolean nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new);
+gboolean nm_ndisc_complete_and_add_address (NMNDisc *ndisc, const NMNDiscAddress *new, gint32 now_s);
 gboolean nm_ndisc_add_route                (NMNDisc *ndisc, const NMNDiscRoute *new);
 gboolean nm_ndisc_add_dns_server           (NMNDisc *ndisc, const NMNDiscDNSServer *new);
 gboolean nm_ndisc_add_dns_domain           (NMNDisc *ndisc, const NMNDiscDNSDomain *new);
diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c
index 879eec67..1dd8398c 100644
--- a/src/ndisc/nm-ndisc.c
+++ b/src/ndisc/nm-ndisc.c
@@ -124,6 +124,79 @@ _preference_to_priority (NMIcmpv6RouterPref pref)
 
 /*****************************************************************************/
 
+/* we rely on the fact, that _EXPIRY_INFINITY > any other valid gint64 timestamps. */
+#define _EXPIRY_INFINITY G_MAXINT64
+
+static gint64
+get_expiry_time (guint32 timestamp, guint32 lifetime)
+{
+	nm_assert (timestamp > 0);
+	nm_assert (timestamp <= G_MAXINT32);
+
+	if (lifetime == NM_NDISC_INFINITY)
+		return _EXPIRY_INFINITY;
+	return ((gint64) timestamp) + ((gint64) lifetime);
+}
+
+#define get_expiry(item) \
+	({ \
+		typeof (item) _item = (item); \
+		nm_assert (_item); \
+		get_expiry_time (_item->timestamp, _item->lifetime); \
+	})
+
+#define get_expiry_half(item) \
+	({ \
+		typeof (item) _item = (item); \
+		nm_assert (_item); \
+		(_item->lifetime == NM_NDISC_INFINITY) \
+		  ? _EXPIRY_INFINITY \
+		  : get_expiry_time (_item->timestamp, _item->lifetime / 2); \
+	})
+
+#define get_expiry_preferred(item) \
+	({ \
+		typeof (item) _item = (item); \
+		nm_assert (_item); \
+		get_expiry_time (_item->timestamp, _item->preferred); \
+	})
+
+static gboolean
+expiry_next (gint32 now_s, gint64 expiry_timestamp, gint32 *nextevent)
+{
+	gint32 e;
+
+	if (expiry_timestamp == _EXPIRY_INFINITY)
+		return TRUE;
+	e = MIN (expiry_timestamp, ((gint64) (G_MAXINT32 - 1)));
+	if (now_s >= e)
+		return FALSE;
+	if (nextevent) {
+		if (*nextevent > e)
+			*nextevent = e;
+	}
+	return TRUE;
+}
+
+static const char *
+_get_exp (char *buf, gsize buf_size, gint64 now_ns, gint64 expiry_time)
+{
+	int l;
+
+	if (expiry_time == _EXPIRY_INFINITY)
+		return "permanent";
+	l = g_snprintf (buf, buf_size,
+	                "%.4f",
+	                ((double) ((expiry_time * NM_UTILS_NS_PER_SECOND) - now_ns)) / ((double) NM_UTILS_NS_PER_SECOND));
+	nm_assert (l < buf_size);
+	return buf;
+}
+
+#define get_exp(buf, now_ns, item) \
+	_get_exp ((buf), G_N_ELEMENTS (buf), (now_ns), (get_expiry (item)))
+
+/*****************************************************************************/
+
 NMPNetns *
 nm_ndisc_netns_get (NMNDisc *self)
 {
@@ -264,9 +337,12 @@ nm_ndisc_add_gateway (NMNDisc *ndisc, const NMNDiscGateway *new)
 				continue;
 			}
 
+			if (get_expiry (item) == get_expiry (new))
+				return FALSE;
+
 			*item = *new;
 			_ASSERT_data_gateways (rdata);
-			return FALSE;
+			return TRUE;
 		}
 
 		/* Put before less preferable gateways. */
@@ -343,33 +419,101 @@ complete_address (NMNDisc *ndisc, NMNDiscAddress *addr)
 }
 
 static gboolean
-nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new)
+nm_ndisc_add_address (NMNDisc *ndisc,
+                      const NMNDiscAddress *new,
+                      gint32 now_s,
+                      gboolean from_ra)
 {
 	NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
 	NMNDiscDataInternal *rdata = &priv->rdata;
+	NMNDiscAddress new2;
+	NMNDiscAddress *existing = NULL;
 	guint i;
 
 	nm_assert (new);
 	nm_assert (new->timestamp > 0 && new->timestamp < G_MAXINT32);
 	nm_assert (!IN6_IS_ADDR_UNSPECIFIED (&new->address));
 	nm_assert (!IN6_IS_ADDR_LINKLOCAL (&new->address));
+	nm_assert (new->preferred <= new->lifetime);
+	nm_assert (!from_ra || now_s > 0);
 
 	for (i = 0; i < rdata->addresses->len; i++) {
 		NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
 
-		if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) {
-			gboolean changed;
+		if (from_ra) {
+			/* RFC4862 5.5.3.d, we find an existing address with the same prefix.
+			 * (note that all prefixes at this point have implicity length /64). */
+			if (memcmp (&item->address, &new->address, 8) == 0) {
+				existing = item;
+				break;
+			}
+		} else {
+			if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) {
+				existing = item;
+				break;
+			}
+		}
+	}
 
-			if (new->lifetime == 0) {
-				g_array_remove_index (rdata->addresses, i);
-				return TRUE;
+	if (existing) {
+		if (from_ra) {
+			const gint32 NM_NDISC_PREFIX_LFT_MIN = 7200; /* seconds, RFC4862 5.5.3.e */
+			gint64 old_expiry_lifetime, old_expiry_preferred;
+
+			old_expiry_lifetime = get_expiry (existing);
+			old_expiry_preferred = get_expiry_preferred (existing);
+
+			if (new->lifetime == NM_NDISC_INFINITY)
+				existing->lifetime = NM_NDISC_INFINITY;
+			else {
+				gint64 new_lifetime, remaining_lifetime;
+
+				/* see RFC4862 5.5.3.e */
+				if (existing->lifetime == NM_NDISC_INFINITY)
+					remaining_lifetime = G_MAXINT64;
+				else
+					remaining_lifetime = ((gint64) existing->timestamp) + ((gint64) existing->lifetime) - ((gint64) now_s);
+				new_lifetime = ((gint64) new->timestamp) + ((gint64) new->lifetime) - ((gint64) now_s);
+
+				if (   new_lifetime > (gint64) NM_NDISC_PREFIX_LFT_MIN
+				    || new_lifetime > remaining_lifetime) {
+					existing->timestamp = now_s;
+					existing->lifetime = CLAMP (new_lifetime, (gint64) 0, (gint64) (G_MAXUINT32 - 1));
+				} else if (remaining_lifetime <= (gint64) NM_NDISC_PREFIX_LFT_MIN) {
+					/* keep the current lifetime. */
+				} else {
+					existing->timestamp = now_s;
+					existing->lifetime = NM_NDISC_PREFIX_LFT_MIN;
+				}
 			}
 
-			changed = item->timestamp + item->lifetime  != new->timestamp + new->lifetime ||
-			          item->timestamp + item->preferred != new->timestamp + new->preferred;
-			*item = *new;
-			return changed;
+			if (new->preferred == NM_NDISC_INFINITY) {
+				nm_assert (existing->lifetime == NM_NDISC_INFINITY);
+				existing->preferred = new->preferred;
+			} else {
+				existing->preferred = NM_CLAMP (((gint64) new->timestamp) + ((gint64) new->preferred) - ((gint64) existing->timestamp),
+				                                0, G_MAXUINT32 - 1);
+				if (existing->lifetime != NM_NDISC_INFINITY)
+					existing->preferred = MIN (existing->preferred, existing->lifetime);
+			}
+
+			return    old_expiry_lifetime != get_expiry (existing)
+			       || old_expiry_preferred != get_expiry_preferred (existing);
 		}
+
+		if (new->lifetime == 0) {
+			g_array_remove_index (rdata->addresses, i);
+			return TRUE;
+		}
+
+		if (   get_expiry (existing) == get_expiry (new)
+		    && get_expiry_preferred (existing) == get_expiry_preferred (new))
+			return FALSE;
+
+		existing->timestamp = new->timestamp;
+		existing->lifetime = new->lifetime;
+		existing->preferred = new->preferred;
+		return TRUE;
 	}
 
 	/* we create at most max_addresses autoconf addresses. This is different from
@@ -380,18 +524,27 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new)
 	    && rdata->addresses->len >= priv->max_addresses)
 		return FALSE;
 
-	if (new->lifetime)
-		g_array_append_val (rdata->addresses, *new);
-	return !!new->lifetime;
+	if (new->lifetime == 0)
+		return FALSE;
+
+	if (from_ra) {
+		new2 = *new;
+		new2.dad_counter = 0;
+		if (!complete_address (ndisc, &new2))
+			return FALSE;
+		new = &new2;
+	}
+
+	g_array_append_val (rdata->addresses, *new);
+	return TRUE;
 }
 
 gboolean
-nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new)
+nm_ndisc_complete_and_add_address (NMNDisc *ndisc,
+                                   const NMNDiscAddress *new,
+                                   gint32 now_s)
 {
-	if (!complete_address (ndisc, new))
-		return FALSE;
-
-	return nm_ndisc_add_address (ndisc, new);
+	return nm_ndisc_add_address (ndisc, new, now_s, TRUE);
 }
 
 gboolean
@@ -419,7 +572,8 @@ nm_ndisc_add_route (NMNDisc *ndisc, const NMNDiscRoute *new)
 	for (i = 0; i < rdata->routes->len; ) {
 		NMNDiscRoute *item = &g_array_index (rdata->routes, NMNDiscRoute, i);
 
-		if (IN6_ARE_ADDR_EQUAL (&item->network, &new->network) && item->plen == new->plen) {
+		if (   IN6_ARE_ADDR_EQUAL (&item->network, &new->network)
+		    && item->plen == new->plen) {
 			if (new->lifetime == 0) {
 				g_array_remove_index (rdata->routes, i);
 				return TRUE;
@@ -430,8 +584,12 @@ nm_ndisc_add_route (NMNDisc *ndisc, const NMNDiscRoute *new)
 				continue;
 			}
 
-			memcpy (item, new, sizeof (*new));
-			return FALSE;
+			if (   get_expiry (item) == get_expiry (new)
+			    && IN6_ARE_ADDR_EQUAL (&item->gateway, &new->gateway))
+				return FALSE;
+
+			*item = *new;
+			return TRUE;
 		}
 
 		/* Put before less preferable routes. */
@@ -470,11 +628,12 @@ nm_ndisc_add_dns_server (NMNDisc *ndisc, const NMNDiscDNSServer *new)
 				g_array_remove_index (rdata->dns_servers, i);
 				return TRUE;
 			}
-			if (item->timestamp != new->timestamp || item->lifetime != new->lifetime) {
-				*item = *new;
-				return TRUE;
-			}
-			return FALSE;
+
+			if (get_expiry (item) == get_expiry (new))
+				return FALSE;
+
+			*item = *new;
+			return TRUE;
 		}
 	}
 
@@ -499,20 +658,17 @@ nm_ndisc_add_dns_domain (NMNDisc *ndisc, const NMNDiscDNSDomain *new)
 		item = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i);
 
 		if (!g_strcmp0 (item->domain, new->domain)) {
-			gboolean changed;
-
 			if (new->lifetime == 0) {
 				g_array_remove_index (rdata->dns_domains, i);
 				return TRUE;
 			}
 
-			changed = (item->timestamp != new->timestamp ||
-			           item->lifetime != new->lifetime);
-			if (changed) {
-				item->timestamp = new->timestamp;
-				item->lifetime = new->lifetime;
-			}
-			return changed;
+			if (get_expiry (item) == get_expiry (new))
+				return FALSE;
+
+			item->timestamp = new->timestamp;
+			item->lifetime = new->lifetime;
+			return TRUE;
 		}
 	}
 
@@ -686,7 +842,7 @@ nm_ndisc_set_config (NMNDisc *ndisc,
 	guint i;
 
 	for (i = 0; i < addresses->len; i++) {
-		if (nm_ndisc_add_address (ndisc, &g_array_index (addresses, NMNDiscAddress, i)))
+		if (nm_ndisc_add_address (ndisc, &g_array_index (addresses, NMNDiscAddress, i), 0, FALSE))
 			changed = TRUE;
 	}
 
@@ -860,57 +1016,6 @@ dhcp_level_to_string (NMNDiscDHCPLevel dhcp_level)
 	}
 }
 
-static gint32
-get_expiry_time (guint32 timestamp, guint32 lifetime)
-{
-	gint64 t;
-
-	/* timestamp is supposed to come from nm_utils_get_monotonic_timestamp_s().
-	 * It is expected to be within a certain range. */
-	nm_assert (timestamp > 0);
-	nm_assert (timestamp <= G_MAXINT32);
-
-	if (lifetime == NM_NDISC_INFINITY)
-		return G_MAXINT32;
-
-	t = (gint64) timestamp + (gint64) lifetime;
-	return CLAMP (t, 0, G_MAXINT32 - 1);
-}
-
-#define get_expiry(item) \
-	({ \
-		typeof (item) _item = (item); \
-		nm_assert (_item); \
-		get_expiry_time ((_item->timestamp), (_item->lifetime)); \
-	})
-
-#define get_expiry_half(item) \
-	({ \
-		typeof (item) _item = (item); \
-		nm_assert (_item); \
-		get_expiry_time ((_item->timestamp),\
-		                 (_item->lifetime) == NM_NDISC_INFINITY \
-		                   ? NM_NDISC_INFINITY \
-		                   : (_item->lifetime) / 2); \
-	})
-
-static const char *
-_get_exp (char *buf, gsize buf_size, gint64 now_ns, gint32 expiry_time)
-{
-	int l;
-
-	if (expiry_time == G_MAXINT32)
-		return "permanent";
-	l = g_snprintf (buf, buf_size,
-	                "%.4f",
-	                ((double) ((expiry_time * NM_UTILS_NS_PER_SECOND) - now_ns)) / ((double) NM_UTILS_NS_PER_SECOND));
-	nm_assert (l < buf_size);
-	return buf;
-}
-
-#define get_exp(buf, now_ns, item) \
-	_get_exp ((buf), G_N_ELEMENTS (buf), (now_ns), (get_expiry (item)))
-
 static void
 _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed)
 {
@@ -984,17 +1089,12 @@ clean_gateways (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *n
 	for (i = 0; i < rdata->gateways->len; ) {
 		NMNDiscGateway *item = &g_array_index (rdata->gateways, NMNDiscGateway, i);
 
-		if (item->lifetime != NM_NDISC_INFINITY) {
-			gint32 expiry = get_expiry (item);
-
-			if (now >= expiry) {
-				g_array_remove_index (rdata->gateways, i);
-				*changed |= NM_NDISC_CONFIG_GATEWAYS;
-				continue;
-			}
-			if (*nextevent > expiry)
-				*nextevent = expiry;
+		if (!expiry_next (now, get_expiry (item), nextevent)) {
+			g_array_remove_index (rdata->gateways, i);
+			*changed |= NM_NDISC_CONFIG_GATEWAYS;
+			continue;
 		}
+
 		i++;
 	}
 
@@ -1012,17 +1112,12 @@ clean_addresses (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *
 	for (i = 0; i < rdata->addresses->len; ) {
 		const NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
 
-		if (item->lifetime != NM_NDISC_INFINITY) {
-			gint32 expiry = get_expiry (item);
-
-			if (now >= expiry) {
-				g_array_remove_index (rdata->addresses, i);
-				*changed |= NM_NDISC_CONFIG_ADDRESSES;
-				continue;
-			}
-			if (*nextevent > expiry)
-				*nextevent = expiry;
+		if (!expiry_next (now, get_expiry (item), nextevent)) {
+			g_array_remove_index (rdata->addresses, i);
+			*changed |= NM_NDISC_CONFIG_ADDRESSES;
+			continue;
 		}
+
 		i++;
 	}
 }
@@ -1038,17 +1133,12 @@ clean_routes (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nex
 	for (i = 0; i < rdata->routes->len; ) {
 		NMNDiscRoute *item = &g_array_index (rdata->routes, NMNDiscRoute, i);
 
-		if (item->lifetime != NM_NDISC_INFINITY) {
-			gint32 expiry = get_expiry (item);
-
-			if (now >= expiry) {
-				g_array_remove_index (rdata->routes, i);
-				*changed |= NM_NDISC_CONFIG_ROUTES;
-				continue;
-			}
-			if (*nextevent > expiry)
-				*nextevent = expiry;
+		if (!expiry_next (now, get_expiry (item), nextevent)) {
+			g_array_remove_index (rdata->routes, i);
+			*changed |= NM_NDISC_CONFIG_ROUTES;
+			continue;
 		}
+
 		i++;
 	}
 }
@@ -1063,18 +1153,16 @@ clean_dns_servers (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32
 
 	for (i = 0; i < rdata->dns_servers->len; ) {
 		NMNDiscDNSServer *item = &g_array_index (rdata->dns_servers, NMNDiscDNSServer, i);
+		gint64 refresh;
 
-		if (item->lifetime != NM_NDISC_INFINITY) {
-			gint32 expiry = get_expiry (item);
-			gint32 refresh;
-
-			if (now >= expiry) {
+		refresh = get_expiry_half (item);
+		if (refresh != _EXPIRY_INFINITY) {
+			if (!expiry_next (now, get_expiry (item), NULL)) {
 				g_array_remove_index (rdata->dns_servers, i);
 				*changed |= NM_NDISC_CONFIG_DNS_SERVERS;
 				continue;
 			}
 
-			refresh = get_expiry_half (item);
 			if (now >= refresh)
 				solicit_routers (ndisc);
 			else if (*nextevent > refresh)
@@ -1094,18 +1182,16 @@ clean_dns_domains (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32
 
 	for (i = 0; i < rdata->dns_domains->len; ) {
 		NMNDiscDNSDomain *item = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i);
+		gint64 refresh;
 
-		if (item->lifetime != NM_NDISC_INFINITY) {
-			gint32 expiry = get_expiry (item);
-			gint32 refresh;
-
-			if (now >= expiry) {
+		refresh = get_expiry_half (item);
+		if (refresh != _EXPIRY_INFINITY) {
+			if (!expiry_next (now, get_expiry (item), NULL)) {
 				g_array_remove_index (rdata->dns_domains, i);
 				*changed |= NM_NDISC_CONFIG_DNS_DOMAINS;
 				continue;
 			}
 
-			refresh = get_expiry_half (item);
 			if (now >= refresh)
 				solicit_routers (ndisc);
 			else if (*nextevent > refresh)
@@ -1132,9 +1218,6 @@ check_timestamps (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed)
 	clean_dns_servers (ndisc, now, &changed, &nextevent);
 	clean_dns_domains (ndisc, now, &changed, &nextevent);
 
-	if (changed)
-		nm_ndisc_emit_config_change (ndisc, changed);
-
 	if (nextevent != G_MAXINT32) {
 		if (nextevent <= now)
 			g_return_if_reached ();
@@ -1142,6 +1225,9 @@ check_timestamps (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed)
 		       (int) (nextevent - now));
 		priv->timeout_id = g_timeout_add_seconds (nextevent - now, timeout_cb, ndisc);
 	}
+
+	if (changed)
+		nm_ndisc_emit_config_change (ndisc, changed);
 }
 
 static gboolean
diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h
index fdc5615f..73eef368 100644
--- a/src/ndisc/nm-ndisc.h
+++ b/src/ndisc/nm-ndisc.h
@@ -58,6 +58,9 @@ typedef enum {
 	NM_NDISC_DHCP_LEVEL_MANAGED
 } NMNDiscDHCPLevel;
 
+/* we rely on the fact that NM_NDISC_INFINITY is the largest possible
+ * time duration (G_MAXUINT32) and that the range of finite values
+ * goes from 0 to G_MAXUINT32-1. */
 #define NM_NDISC_INFINITY  G_MAXUINT32
 
 struct _NMNDiscGateway {
diff --git a/src/ndisc/tests/test-ndisc-fake.c b/src/ndisc/tests/test-ndisc-fake.c
index e99d2fc5..268f3b49 100644
--- a/src/ndisc/tests/test-ndisc-fake.c
+++ b/src/ndisc/tests/test-ndisc-fake.c
@@ -233,8 +233,9 @@ test_everything_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed
 
 		g_assert_cmpint (rdata->gateways_n, ==, 1);
 		match_gateway (rdata, 0, "fe80::2", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM);
-		g_assert_cmpint (rdata->addresses_n, ==, 1);
-		match_address (rdata, 0, "2001:db8:a:b::1", data->timestamp1, 10, 10);
+		g_assert_cmpint (rdata->addresses_n, ==, 2);
+		match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 0);
+		match_address (rdata, 1, "2001:db8:a:b::1", data->timestamp1, 10, 10);
 		g_assert_cmpint (rdata->routes_n, ==, 1);
 		match_route (rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1, 10, 10);
 		g_assert_cmpint (rdata->dns_servers_n, ==, 1);
@@ -384,7 +385,7 @@ test_preference_changed_cb (NMNDisc *ndisc, const NMNDiscData *rdata, guint chan
 		match_gateway (rdata, 0, "fe80::1", data->timestamp1 + 2, 10, NM_ICMPV6_ROUTER_PREF_HIGH);
 		match_gateway (rdata, 1, "fe80::2", data->timestamp1 + 1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM);
 		g_assert_cmpint (rdata->addresses_n, ==, 2);
-		match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1 + 2, 10, 10);
+		match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1 + 3, 9, 9);
 		match_address (rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10);
 		g_assert_cmpint (rdata->routes_n, ==, 2);
 		match_route (rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1 + 2, 10, 15);
diff --git a/src/nm-config.c b/src/nm-config.c
index 102070e9..628eca4f 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -51,7 +51,8 @@ struct NMConfigCmdLineOptions {
 	char *state_file;
 	char *no_auto_default_file;
 	char *plugins;
-	gboolean configure_and_quit;
+	NMConfigConfigureAndQuitType configure_and_quit;
+
 	gboolean is_debug;
 	char *connectivity_uri;
 
@@ -105,7 +106,7 @@ typedef struct {
 	char *log_level;
 	char *log_domains;
 
-	gboolean configure_and_quit;
+	NMConfigConfigureAndQuitType configure_and_quit;
 
 	char **atomic_section_prefixes;
 
@@ -323,7 +324,7 @@ nm_config_get_log_domains (NMConfig *config)
 	return NM_CONFIG_GET_PRIVATE (config)->log_domains;
 }
 
-gboolean
+NMConfigConfigureAndQuitType
 nm_config_get_configure_and_quit (NMConfig *config)
 {
 	return NM_CONFIG_GET_PRIVATE (config)->configure_and_quit;
@@ -392,9 +393,16 @@ no_auto_default_to_file (const char *no_auto_default_file, const char *const*no_
 gboolean
 nm_config_get_no_auto_default_for_device (NMConfig *self, NMDevice *device)
 {
+	NMConfigPrivate *priv;
+
 	g_return_val_if_fail (NM_IS_CONFIG (self), FALSE);
 
-	return nm_config_data_get_no_auto_default_for_device (NM_CONFIG_GET_PRIVATE (self)->config_data, device);
+	priv = NM_CONFIG_GET_PRIVATE (self);
+
+	if (priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
+		return TRUE;
+
+	return nm_config_data_get_no_auto_default_for_device (priv->config_data, device);
 }
 
 void
@@ -457,7 +465,7 @@ _nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli)
 	g_clear_pointer (&cli->intern_config_file, g_free);
 	g_clear_pointer (&cli->state_file, g_free);
 	g_clear_pointer (&cli->plugins, g_free);
-	cli->configure_and_quit = FALSE;
+	cli->configure_and_quit = NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED;
 	cli->is_debug = FALSE;
 	g_clear_pointer (&cli->connectivity_uri, g_free);
 	g_clear_pointer (&cli->connectivity_response, g_free);
@@ -509,34 +517,72 @@ nm_config_cmd_line_options_free (NMConfigCmdLineOptions *cli)
 	g_free (cli);
 }
 
+static NMConfigConfigureAndQuitType
+string_to_configure_and_quit (const char *value, GError **error)
+{
+	NMConfigConfigureAndQuitType ret;
+
+	if (value == NULL)
+		return NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED;
+
+	if (strcmp (value, "initrd") == 0)
+		return NM_CONFIG_CONFIGURE_AND_QUIT_INITRD;
+
+	ret = nm_config_parse_boolean (value, NM_CONFIG_CONFIGURE_AND_QUIT_INVALID);
+	if (ret == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID)
+		g_set_error (error, 1, 0, N_("'%s' is not valid"), value);
+
+	return ret;
+}
+
+static gboolean
+parse_configure_and_quit (const char *option_name, const char *value, gpointer user_data, GError **error)
+{
+	NMConfigCmdLineOptions *cli = user_data;
+
+	if (value == NULL)
+		cli->configure_and_quit = NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED;
+	else
+		cli->configure_and_quit = string_to_configure_and_quit (value, error);
+
+	if (cli->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) {
+		g_prefix_error (error, N_("Bad '%s' option: "), option_name);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 void
 nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli,
                                            GOptionContext *opt_ctx)
 {
+	GOptionGroup *group;
+	GOptionEntry config_options[] = {
+		{ "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), DEFAULT_CONFIG_MAIN_FILE },
+		{ "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), DEFAULT_CONFIG_DIR },
+		{ "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), DEFAULT_SYSTEM_CONFIG_DIR },
+		{ "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), DEFAULT_INTERN_CONFIG_FILE },
+		{ "state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), DEFAULT_STATE_FILE },
+		{ "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), DEFAULT_NO_AUTO_DEFAULT_FILE },
+		{ "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), NM_CONFIG_DEFAULT_MAIN_PLUGINS },
+		{ "configure-and-quit", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_configure_and_quit, N_("Quit after initial configuration"), NULL },
+		{ "debug", 'd', 0, G_OPTION_ARG_NONE, &cli->is_debug, N_("Don't become a daemon, and log to stderr"), NULL },
+
+			/* These three are hidden for now, and should eventually just go away. */
+		{ "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" },
+		{ "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), G_STRINGIFY (NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL) },
+		{ "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE },
+		{ 0 },
+	};
+
 	g_return_if_fail (opt_ctx);
 	g_return_if_fail (cli);
 
-	{
-		GOptionEntry config_options[] = {
-			{ "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), DEFAULT_CONFIG_MAIN_FILE },
-			{ "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), DEFAULT_CONFIG_DIR },
-			{ "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), DEFAULT_SYSTEM_CONFIG_DIR },
-			{ "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), DEFAULT_INTERN_CONFIG_FILE },
-			{ "state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), DEFAULT_STATE_FILE },
-			{ "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), DEFAULT_NO_AUTO_DEFAULT_FILE },
-			{ "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), NM_CONFIG_DEFAULT_MAIN_PLUGINS },
-			{ "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL },
-			{ "debug", 'd', 0, G_OPTION_ARG_NONE, &cli->is_debug, N_("Don't become a daemon, and log to stderr"), NULL },
-
-				/* These three are hidden for now, and should eventually just go away. */
-			{ "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" },
-			{ "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), G_STRINGIFY (NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL) },
-			{ "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE },
-			{ 0 },
-		};
+	group = g_option_group_new ("nm", N_("NetworkManager options" ), N_("Show NetworkManager options"), cli, NULL);
 
-		g_option_context_add_main_entries (opt_ctx, config_options, NULL);
-	}
+	g_option_group_add_entries (group, config_options);
+	g_option_context_add_group (opt_ctx, group);
 }
 
 /*****************************************************************************/
@@ -892,7 +938,7 @@ read_base_config (GKeyFile *keyfile,
 	}
 
 	if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
-		_LOGW ("Old default config file invalid: %s\n",
+		_LOGW ("Old default config file invalid: %s",
 		       my_error->message);
 	}
 	g_clear_error (&my_error);
@@ -904,7 +950,7 @@ read_base_config (GKeyFile *keyfile,
 	}
 
 	if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
-		_LOGW ("Default config file invalid: %s\n",
+		_LOGW ("Default config file invalid: %s",
 		       my_error->message);
 		g_propagate_error (error, my_error);
 		return FALSE;
@@ -915,7 +961,7 @@ read_base_config (GKeyFile *keyfile,
 	 * config file path.
 	 */
 	*out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE);
-	_LOGI ("No config file found or given; using %s\n",
+	_LOGI ("No config file found or given; using %s",
 	       DEFAULT_CONFIG_MAIN_FILE);
 	return TRUE;
 }
@@ -1046,19 +1092,36 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
 
 	/* Merge settings from command line. They overwrite everything read from
 	 * config files. */
-	if (cli && cli->plugins) {
-		/* plugins is a string list. Set the value directly, so the user has to do proper escaping
-		 * on the command line. */
-		g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", cli->plugins);
-	}
-	if (cli && cli->configure_and_quit)
-		g_key_file_set_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", TRUE);
-	if (cli && cli->connectivity_uri && cli->connectivity_uri[0])
-		g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", cli->connectivity_uri);
-	if (cli && cli->connectivity_interval >= 0)
-		g_key_file_set_integer (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", cli->connectivity_interval);
-	if (cli && cli->connectivity_response && cli->connectivity_response[0])
-		g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", cli->connectivity_response);
+
+	if (cli) {
+		if (cli->plugins) {
+			/* plugins is a string list. Set the value directly, so the user has to do proper escaping
+			 * on the command line. */
+			g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", cli->plugins);
+		}
+
+		switch (cli->configure_and_quit) {
+		case NM_CONFIG_CONFIGURE_AND_QUIT_INVALID:
+			g_assert_not_reached ();
+			break;
+		case NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED:
+			/* do nothing */
+			break;
+		case NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED:
+			g_key_file_set_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", TRUE);
+			break;
+		case NM_CONFIG_CONFIGURE_AND_QUIT_INITRD:
+			g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", "initrd");
+			break;
+		}
+
+		if (cli->connectivity_uri && cli->connectivity_uri[0])
+			g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", cli->connectivity_uri);
+		if (cli->connectivity_interval >= 0)
+			g_key_file_set_integer (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", cli->connectivity_interval);
+		if (cli->connectivity_response && cli->connectivity_response[0])
+			g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", cli->connectivity_response);
+	}
 
 	if (out_config_description) {
 		GString *str;
@@ -1837,6 +1900,9 @@ state_write (NMConfig *self)
 	GString *str;
 	GError *error = NULL;
 
+	if (priv->configure_and_quit != NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED)
+		return;
+
 	filename = state_get_filename (&priv->cli);
 
 	if (!filename) {
@@ -1931,6 +1997,7 @@ _nm_config_state_set (NMConfig *self,
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED            "nm-owned"
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED   "route-metric-default-aspired"
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE "route-metric-default-effective"
+#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH           "root-path"
 
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType,
 	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
@@ -2128,7 +2195,8 @@ nm_config_device_state_write (int ifindex,
                               const char *connection_uuid,
                               int nm_owned,
                               guint32 route_metric_default_aspired,
-                              guint32 route_metric_default_effective)
+                              guint32 route_metric_default_effective,
+                              const char *root_path)
 {
 	char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60];
 	GError *local = NULL;
@@ -2182,19 +2250,26 @@ nm_config_device_state_write (int ifindex,
 			                      route_metric_default_aspired);
 		}
 	}
+	if (root_path) {
+		g_key_file_set_string (kf,
+		                       DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+		                       DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH,
+		                       root_path);
+	}
 
 	if (!g_key_file_save_to_file (kf, path, &local)) {
 		_LOGW ("device-state: write #%d (%s) failed: %s", ifindex, path, local->message);
 		g_error_free (local);
 		return FALSE;
 	}
-	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"",
+	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"%s%s%s",
 	       ifindex, path,
 	       _device_state_managed_type_to_str (managed),
 	       NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""),
 	       NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""),
 	       route_metric_default_aspired,
-	       route_metric_default_effective);
+	       route_metric_default_effective,
+	       NM_PRINT_FMT_QUOTED (root_path, ", root-path=", root_path, "", ""));
 	return TRUE;
 }
 
@@ -2332,7 +2407,11 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags)
 		                     (const char *const*) priv->atomic_section_prefixes, NULL);
 	}
 
-	new_data = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile, keyfile_intern);
+	new_data = nm_config_data_new (config_main_file,
+	                               config_description,
+	                               (const char *const*) no_auto_default,
+	                               keyfile,
+	                               keyfile_intern);
 	g_free (config_main_file);
 	g_free (config_description);
 	g_key_file_unref (keyfile);
@@ -2482,6 +2561,7 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
 	char *config_main_file = NULL;
 	char *config_description = NULL;
 	gs_strfreev char **no_auto_default = NULL;
+	gs_free char *configure_and_quit = NULL;
 	gboolean intern_config_needs_rewrite;
 	const char *s;
 
@@ -2528,7 +2608,10 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
 	priv->log_level = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "level", NULL));
 	priv->log_domains = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains", NULL));
 
-	priv->configure_and_quit = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", FALSE);
+	configure_and_quit = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", NULL));
+	priv->configure_and_quit = string_to_configure_and_quit (configure_and_quit, error);
+	if (priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID)
+		return FALSE;
 
 	no_auto_default = no_auto_default_from_file (priv->no_auto_default_file);
 
@@ -2536,12 +2619,17 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
 	                                     keyfile,
 	                                     (const char *const*) priv->atomic_section_prefixes,
 	                                     &intern_config_needs_rewrite);
-	if (intern_config_needs_rewrite) {
+	if (   intern_config_needs_rewrite
+	    && priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) {
 		intern_config_write (priv->intern_config_file, keyfile_intern, keyfile,
 		                     (const char *const*) priv->atomic_section_prefixes, NULL);
 	}
 
-	priv->config_data_orig = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile, keyfile_intern);
+	priv->config_data_orig = nm_config_data_new (config_main_file,
+	                                             config_description,
+	                                             (const char *const*) no_auto_default,
+	                                             keyfile,
+	                                             keyfile_intern);
 
 	priv->config_data = g_object_ref (priv->config_data_orig);
 
diff --git a/src/nm-config.h b/src/nm-config.h
index 1b013ff3..c65572ce 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -101,6 +101,13 @@ typedef enum {
 	NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED,
 } NMConfigRunStatePropertyType;
 
+typedef enum {
+	NM_CONFIG_CONFIGURE_AND_QUIT_INVALID = -1,
+	NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED = FALSE,
+	NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED = TRUE,
+	NM_CONFIG_CONFIGURE_AND_QUIT_INITRD,
+} NMConfigConfigureAndQuitType;
+
 typedef struct {
 	bool net_enabled;
 	bool wifi_enabled;
@@ -127,7 +134,7 @@ NMConfigData *nm_config_get_data_orig (NMConfig *config);
 gboolean nm_config_get_monitor_connection_files (NMConfig *config);
 const char *nm_config_get_log_level (NMConfig *config);
 const char *nm_config_get_log_domains (NMConfig *config);
-gboolean nm_config_get_configure_and_quit (NMConfig *config);
+NMConfigConfigureAndQuitType nm_config_get_configure_and_quit (NMConfig *config);
 gboolean nm_config_get_is_debug (NMConfig *config);
 
 gboolean nm_config_get_first_start (NMConfig *config);
@@ -236,7 +243,8 @@ gboolean nm_config_device_state_write (int ifindex,
                                        const char *connection_uuid,
                                        int nm_owned,
                                        guint32 route_metric_default_aspired,
-                                       guint32 route_metric_default_effective);
+                                       guint32 route_metric_default_effective,
+                                       const char *root_path);
 
 void nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes);
 
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 3b779677..8a6e955a 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -46,8 +46,10 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, int /*NMConnectivityState*/
 	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_PORTAL,   "PORTAL"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL,     "FULL"),
 
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_ERROR,    "ERROR"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FAKE,     "FAKE"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_ERROR,     "ERROR"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FAKE,      "FAKE"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_CANCELLED, "CANCELLED"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_DISPOSING, "DISPOSING"),
 );
 
 const char *
@@ -77,6 +79,10 @@ struct _NMConnectivityCheckHandle {
 	} concheck;
 #endif
 
+	const char *completed_log_message;
+	char *completed_log_message_free;
+	NMConnectivityState completed_state;
+
 	guint timeout_id;
 };
 
@@ -90,6 +96,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
 	CList handles_lst_head;
+	CList completed_handles_lst_head;
 	char *uri;
 	char *response;
 	gboolean enabled;
@@ -142,60 +149,37 @@ NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECT
 /*****************************************************************************/
 
 static void
-cb_data_invoke_callback (NMConnectivityCheckHandle *cb_data,
-                         NMConnectivityState state,
-                         GError *error,
-                         const char *log_message)
+cb_data_complete (NMConnectivityCheckHandle *cb_data,
+                  NMConnectivityState state,
+                  const char *log_message)
 {
-	NMConnectivityCheckCallback callback;
+	NMConnectivity *self;
 
 	nm_assert (cb_data);
 	nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
-
-	callback = cb_data->callback;
-	if (!callback)
-		return;
-
-	cb_data->callback = NULL;
-
+	nm_assert (cb_data->callback);
+	nm_assert (state != NM_CONNECTIVITY_UNKNOWN);
 	nm_assert (log_message);
 
-	_LOG2D ("check completed: %s; %s",
-	        nm_connectivity_state_to_string (state),
-	        log_message);
-
-	callback (cb_data->self,
-	          cb_data,
-	          state,
-	          error,
-	          cb_data->user_data);
-}
-
-static void
-cb_data_free (NMConnectivityCheckHandle *cb_data,
-              NMConnectivityState state,
-              GError *error,
-              const char *log_message)
-{
-	NMConnectivity *self;
-
-	nm_assert (cb_data);
-
 	self = cb_data->self;
 
-	nm_assert (NM_IS_CONNECTIVITY (self));
+	/* mark the handle as completing. After this point, nm_connectivity_check_cancel()
+	 * is no longer possible. */
+	cb_data->self = NULL;
 
-	c_list_unlink (&cb_data->handles_lst);
+	c_list_unlink_stale (&cb_data->handles_lst);
 
 #if WITH_CONCHECK
 	if (cb_data->concheck.curl_ehandle) {
 		NMConnectivityPrivate *priv;
 
 		/* Contrary to what cURL manual claim it is *not* safe to remove
-		 * the easy handle "at any moment"; specifically not from the
-		 * write function. Thus here we just dissociate the cb_data from
-		 * the easy handle and the easy handle will be cleaned up when the
-		 * message goes to CURLMSG_DONE in _con_curl_check_connectivity(). */
+		 * the easy handle "at any moment"; specifically it's not safe to
+		 * remove *any* handle from within a libcurl callback. That is
+		 * why we queue completed handles in this case.
+		 *
+		 * cb_data_complete() is however only called *not* from within a
+		 * libcurl callback. So, this is fine. */
 		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEFUNCTION, NULL);
 		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEDATA, NULL);
 		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HEADERFUNCTION, NULL);
@@ -214,7 +198,18 @@ cb_data_free (NMConnectivityCheckHandle *cb_data,
 
 	nm_clear_g_source (&cb_data->timeout_id);
 
-	cb_data_invoke_callback (cb_data, state, error, log_message);
+	_LOG2D ("check completed: %s; %s",
+	        nm_connectivity_state_to_string (state),
+	        log_message);
+
+	cb_data->callback (self,
+	                   cb_data,
+	                   state,
+	                   cb_data->user_data);
+
+	/* Note: self might be a danling pointer at this point. It must not be used
+	 * after this point, and all callers must either take a reference first, or
+	 * not use the self pointer too. */
 
 #if WITH_CONCHECK
 	g_free (cb_data->concheck.response);
@@ -222,12 +217,54 @@ cb_data_free (NMConnectivityCheckHandle *cb_data,
 		g_string_free (cb_data->concheck.recv_msg, TRUE);
 #endif
 	g_free (cb_data->ifspec);
+	if (cb_data->completed_log_message_free)
+		g_free (cb_data->completed_log_message_free);
 	g_slice_free (NMConnectivityCheckHandle, cb_data);
 }
 
 /*****************************************************************************/
 
 #if WITH_CONCHECK
+
+static void
+cb_data_queue_completed (NMConnectivityCheckHandle *cb_data,
+                         NMConnectivityState state,
+                         const char *log_message_static,
+                         char *log_message_take /* take */)
+{
+	nm_assert (cb_data);
+	nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
+	nm_assert (state != NM_CONNECTIVITY_UNKNOWN);
+	nm_assert (log_message_static || log_message_take);
+	nm_assert (cb_data->completed_state == NM_CONNECTIVITY_UNKNOWN);
+	nm_assert (!cb_data->completed_log_message);
+	nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst));
+
+	cb_data->completed_state = state;
+	cb_data->completed_log_message = log_message_static ?: log_message_take;
+	cb_data->completed_log_message_free = log_message_take;
+
+	c_list_unlink_stale (&cb_data->handles_lst);
+	c_list_link_tail (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->completed_handles_lst_head, &cb_data->handles_lst);
+}
+
+static void
+_complete_queued (NMConnectivity *self)
+{
+	NMConnectivity *self_keep_alive = NULL;
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	NMConnectivityCheckHandle *cb_data;
+
+	while ((cb_data = c_list_first_entry (&priv->completed_handles_lst_head, NMConnectivityCheckHandle, handles_lst))) {
+		if (!self_keep_alive)
+			self_keep_alive = g_object_ref (self);
+		cb_data_complete (cb_data,
+		                  cb_data->completed_state,
+		                  cb_data->completed_log_message);
+	}
+	nm_g_object_unref (self_keep_alive);
+}
+
 static const char *
 _check_handle_get_response (NMConnectivityCheckHandle *cb_data)
 {
@@ -265,29 +302,37 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask)
 			continue;
 		}
 
-		if (!cb_data->callback) {
-			/* callback was already invoked earlier. */
-			cb_data_free (cb_data, NM_CONNECTIVITY_UNKNOWN, NULL, NULL);
-		} else if (msg->data.result != CURLE_OK) {
-			gs_free char *log_message = NULL;
+		nm_assert (cb_data);
+		nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
 
-			log_message = g_strdup_printf ("check failed with curl status %d", msg->data.result);
-			cb_data_free (cb_data, NM_CONNECTIVITY_LIMITED, NULL,
-			              log_message);
+		if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
+			/* callback was already invoked earlier. Nothing to do. */
+			continue;
+		}
+
+		if (msg->data.result != CURLE_OK) {
+			cb_data_queue_completed (cb_data,
+			                         NM_CONNECTIVITY_LIMITED,
+			                         NULL,
+			                         g_strdup_printf ("check failed with curl status %d", msg->data.result));
 		} else if (   !((_check_handle_get_response (cb_data))[0])
 		           && (curl_easy_getinfo (msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK)
 		           && response_code == 204) {
 			/* If we got a 204 response code (no content) and we actually
 			 * requested no content, report full connectivity. */
-			cb_data_free (cb_data, NM_CONNECTIVITY_FULL, NULL,
-			              "no content, as expected");
+			cb_data_queue_completed (cb_data,
+			                         NM_CONNECTIVITY_FULL,
+			                         "no content, as expected",
+			                         NULL);
 		} else {
 			/* If we get here, it means that easy_write_cb() didn't read enough
 			 * bytes to be able to do a match, or that we were asking for no content
 			 * (204 response code) and we actually got some. Either way, that is
 			 * an indication of a captive portal */
-			cb_data_free (cb_data, NM_CONNECTIVITY_PORTAL, NULL,
-			              "unexpected short response");
+			cb_data_queue_completed (cb_data,
+			                         NM_CONNECTIVITY_PORTAL,
+			                         "unexpected short response",
+			                         NULL);
 		}
 	}
 
@@ -306,6 +351,7 @@ _con_curl_timeout_cb (gpointer user_data)
 
 	priv->concheck.curl_timer = 0;
 	_con_curl_check_connectivity (priv->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0);
+	_complete_queued (self);
 	return G_SOURCE_REMOVE;
 }
 
@@ -368,6 +414,8 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_
 			fdp->ev = 0;
 	}
 
+	_complete_queued (self);
+
 	return success ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
 }
 
@@ -419,10 +467,17 @@ easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata)
 	NMConnectivityCheckHandle *cb_data = userdata;
 	size_t len = size * nitems;
 
+	if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
+		/* already completed. */
+		return 0;
+	}
+
 	if (   len >= sizeof (HEADER_STATUS_ONLINE) - 1
 	    && !g_ascii_strncasecmp (buffer, HEADER_STATUS_ONLINE, sizeof (HEADER_STATUS_ONLINE) - 1)) {
-		cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_FULL,
-		                         NULL, "status header found");
+		cb_data_queue_completed (cb_data,
+		                         NM_CONNECTIVITY_FULL,
+		                         "status header found",
+		                         NULL);
 		return 0;
 	}
 
@@ -434,24 +489,33 @@ easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata)
 {
 	NMConnectivityCheckHandle *cb_data = userdata;
 	size_t len = size * nmemb;
-	const char *response = _check_handle_get_response (cb_data);;
+	const char *response;
+
+	if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
+		/* already completed. */
+		return 0;
+	}
 
 	if (!cb_data->concheck.recv_msg)
 		cb_data->concheck.recv_msg = g_string_sized_new (len + 10);
 
 	g_string_append_len (cb_data->concheck.recv_msg, buffer, len);
 
+	response = _check_handle_get_response (cb_data);;
 	if (   response
 	    && cb_data->concheck.recv_msg->len >= strlen (response)) {
 		/* We already have enough data -- check response */
 		if (g_str_has_prefix (cb_data->concheck.recv_msg->str, response)) {
-			cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_FULL, NULL,
-			                         "expected response");
+			cb_data_queue_completed (cb_data,
+			                         NM_CONNECTIVITY_FULL,
+			                         "expected response",
+			                         NULL);
 		} else {
-			cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_PORTAL, NULL,
-			                         "unexpected response");
+			cb_data_queue_completed (cb_data,
+			                         NM_CONNECTIVITY_PORTAL,
+			                         "unexpected response",
+			                         NULL);
 		}
-
 		return 0;
 	}
 
@@ -462,15 +526,11 @@ static gboolean
 _timeout_cb (gpointer user_data)
 {
 	NMConnectivityCheckHandle *cb_data = user_data;
-	NMConnectivity *self;
 
 	nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
+	nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst));
 
-	self = cb_data->self;
-
-	nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (self)->handles_lst_head, &cb_data->handles_lst));
-
-	cb_data_free (cb_data, NM_CONNECTIVITY_LIMITED, NULL, "timeout");
+	cb_data_complete (cb_data, NM_CONNECTIVITY_LIMITED, "timeout");
 	return G_SOURCE_REMOVE;
 }
 #endif
@@ -490,9 +550,9 @@ _idle_cb (gpointer user_data)
 		/* the invocation was with an invalid ifname. It is a fail. */
 		g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
 		             "no interface specified for connectivity check");
-		cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, NULL, "missing interface");
+		cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "missing interface");
 	} else
-		cb_data_free (cb_data, NM_CONNECTIVITY_FAKE, NULL, "fake result");
+		cb_data_complete (cb_data, NM_CONNECTIVITY_FAKE, "fake result");
 	return G_SOURCE_REMOVE;
 }
 
@@ -516,6 +576,7 @@ nm_connectivity_check_start (NMConnectivity *self,
 	c_list_link_tail (&priv->handles_lst_head, &cb_data->handles_lst);
 	cb_data->callback = callback;
 	cb_data->user_data = user_data;
+	cb_data->completed_state = NM_CONNECTIVITY_UNKNOWN;
 
 	if (iface)
 		cb_data->ifspec = g_strdup_printf ("if!%s", iface);
@@ -556,22 +617,13 @@ nm_connectivity_check_start (NMConnectivity *self,
 void
 nm_connectivity_check_cancel (NMConnectivityCheckHandle *cb_data)
 {
-	NMConnectivity *self;
-	gs_free_error GError *error = NULL;
-
 	g_return_if_fail (cb_data);
+	g_return_if_fail (NM_IS_CONNECTIVITY (cb_data->self));
 
-	self = cb_data->self;
-
-	g_return_if_fail (NM_IS_CONNECTIVITY (self));
-	g_return_if_fail (!c_list_is_empty (&cb_data->handles_lst));
-	g_return_if_fail (cb_data->callback);
-
-	nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (self)->handles_lst_head, &cb_data->handles_lst));
+	nm_assert (   c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head,           &cb_data->handles_lst)
+	           || c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->completed_handles_lst_head, &cb_data->handles_lst));
 
-	nm_utils_error_set_cancelled (&error, FALSE, "NMConnectivity");
-
-	cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, error, "cancelled");
+	cb_data_complete (cb_data, NM_CONNECTIVITY_CANCELLED, "cancelled");
 }
 
 /*****************************************************************************/
@@ -684,6 +736,7 @@ nm_connectivity_init (NMConnectivity *self)
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 
 	c_list_init (&priv->handles_lst_head);
+	c_list_init (&priv->completed_handles_lst_head);
 
 	priv->config = g_object_ref (nm_config_get ());
 	g_signal_connect (G_OBJECT (priv->config),
@@ -715,16 +768,13 @@ dispose (GObject *object)
 	NMConnectivity *self = NM_CONNECTIVITY (object);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 	NMConnectivityCheckHandle *cb_data;
-	GError *error = NULL;
-
-again:
-	c_list_for_each_entry (cb_data, &priv->handles_lst_head, handles_lst) {
-		if (!error)
-			nm_utils_error_set_cancelled (&error, TRUE, "NMConnectivity");
-		cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, error, "shutting down");
-		goto again;
-	}
-	g_clear_error (&error);
+
+	nm_assert (c_list_is_empty (&priv->completed_handles_lst_head));
+
+	while ((cb_data = c_list_first_entry (&priv->handles_lst_head,
+	                                      NMConnectivityCheckHandle,
+	                                      handles_lst)))
+		cb_data_complete (cb_data, NM_CONNECTIVITY_DISPOSING, "shutting down");
 
 	g_clear_pointer (&priv->uri, g_free);
 	g_clear_pointer (&priv->response, g_free);
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index df9295e0..178f27ad 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -24,8 +24,10 @@
 
 #include "nm-dbus-interface.h"
 
-#define NM_CONNECTIVITY_ERROR ((NMConnectivityState) -1)
-#define NM_CONNECTIVITY_FAKE  ((NMConnectivityState) -2)
+#define NM_CONNECTIVITY_ERROR     ((NMConnectivityState) -1)
+#define NM_CONNECTIVITY_FAKE      ((NMConnectivityState) -2)
+#define NM_CONNECTIVITY_CANCELLED ((NMConnectivityState) -3)
+#define NM_CONNECTIVITY_DISPOSING ((NMConnectivityState) -4)
 
 #define NM_TYPE_CONNECTIVITY            (nm_connectivity_get_type ())
 #define NM_CONNECTIVITY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTIVITY, NMConnectivity))
@@ -53,7 +55,6 @@ typedef struct _NMConnectivityCheckHandle NMConnectivityCheckHandle;
 typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self,
                                              NMConnectivityCheckHandle *handle,
                                              NMConnectivityState state,
-                                             GError *error,
                                              gpointer user_data);
 
 NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self,
diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c
index 0a421aa9..ca7bde57 100644
--- a/src/nm-dbus-manager.c
+++ b/src/nm-dbus-manager.c
@@ -1480,7 +1480,11 @@ nm_dbus_manager_start (NMDBusManager *self,
 
 	g_return_if_fail (NM_IS_DBUS_MANAGER (self));
 	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	g_return_if_fail (priv->connection);
+
+	if (!priv->connection) {
+		/* Do nothing. We're presumably in the configure-and-quit mode. */
+		return;
+	}
 
 	priv->set_property_handler = set_property_handler;
 	priv->set_property_handler_data = set_property_handler_data;
@@ -1505,29 +1509,17 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self)
 
 	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
 
-	/* we will create the D-Bus connection and registering the name synchronously.
-	 * The reason why that is necessary is because:
-	 *  (1) if we are unable to create a D-Bus connection, it means D-Bus is not
-	 *    available and we run in D-Bus less mode. We do not support creating
-	 *    a D-Bus connection later on. This disconnected mode is useful for initrd
-	 *    (well, currently not yet, but will be).
-	 *  (2) if we are able to create the connection and register the name,
-	 *    all is good and we run with D-Bus. Note that D-Bus disconnects
-	 *    from D-Bus are ignored. Essentially, we do not support restarting
-	 *    D-Bus.
-	 *  (3) if we are able to create the connection but registration fails,
-	 *    it means that something is borked. Quite possibly another NetworkManager
-	 *    instance is running. We need to exit right away.
-	 * To appease (1) and (3), we cannot initalize synchronously, because we need
-	 * to know right away whether another NetworkManager instance is running (3).
-	 **/
-
+	/* Create the D-Bus connection and registering the name synchronously.
+	 * That is necessary because we need to exit right away if we can't
+	 * acquire the name despite connecting to the bus successfully.
+	 * It means that something is gravely broken -- such as another NetworkManager
+	 * instance running. */
 	connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
 	                             NULL,
 	                             &error);
 	if (!connection) {
-		_LOGI ("cannot connect to D-Bus and proceed without (%s)", error->message);
-		return TRUE;
+		_LOGI ("cannot connect to D-Bus: %s", error->message);
+		return FALSE;
 	}
 
 	g_dbus_connection_set_exit_on_close (connection, FALSE);
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index 805c6e4f..6ae2b9d2 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -627,10 +627,10 @@ nm_config_data_get_value (const NMConfigData *config_data, const char *group, co
 	return NULL;
 }
 
-gboolean
+NMConfigConfigureAndQuitType
 nm_config_get_configure_and_quit (NMConfig *config)
 {
-	return TRUE;
+	return NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED;
 }
 
 NMDBusManager *
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 573c479a..6604711c 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -1511,6 +1511,7 @@ nm_ip4_config_subtract (NMIP4Config *dst,
 static gboolean
 _nm_ip4_config_intersect_helper (NMIP4Config *dst,
                                  const NMIP4Config *src,
+                                 gboolean intersect_routes,
                                  guint32 default_route_metric_penalty,
                                  gboolean update_dst)
 {
@@ -1555,6 +1556,9 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
 	/* ignore nameservers */
 
 	/* routes */
+	if (!intersect_routes)
+		goto skip_routes;
+
 	changed = FALSE;
 	new_best_default_route = NULL;
 	nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) {
@@ -1595,6 +1599,7 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
 		_notify (dst, PROP_GATEWAY);
 	}
 
+skip_routes:
 	if (changed) {
 		_notify_routes (dst);
 		result = TRUE;
@@ -1625,9 +1630,10 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
 void
 nm_ip4_config_intersect (NMIP4Config *dst,
                          const NMIP4Config *src,
+                         gboolean intersect_routes,
                          guint32 default_route_metric_penalty)
 {
-	_nm_ip4_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE);
+	_nm_ip4_config_intersect_helper (dst, src, intersect_routes, default_route_metric_penalty, TRUE);
 }
 
 /**
@@ -1648,14 +1654,17 @@ nm_ip4_config_intersect (NMIP4Config *dst,
 NMIP4Config *
 nm_ip4_config_intersect_alloc (const NMIP4Config *a,
                                const NMIP4Config *b,
+                               gboolean intersect_routes,
                                guint32 default_route_metric_penalty)
 {
 	NMIP4Config *a_copy;
 
 	if (_nm_ip4_config_intersect_helper ((NMIP4Config *) a, b,
+	                                     intersect_routes,
 	                                     default_route_metric_penalty, FALSE)) {
 		a_copy = nm_ip4_config_clone (a);
-		_nm_ip4_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE);
+		_nm_ip4_config_intersect_helper (a_copy, b, intersect_routes,
+		                                 default_route_metric_penalty, TRUE);
 		return a_copy;
 	} else
 		return NULL;
@@ -1971,71 +1980,90 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 }
 
 void
-nm_ip4_config_dump (const NMIP4Config *self, const char *detail)
+nm_ip_config_dump (const NMIPConfig *self,
+                   const char *detail,
+                   NMLogLevel level,
+                   NMLogDomain domain)
 {
-	guint32 tmp;
-	guint i;
-	const char *str;
 	NMDedupMultiIter ipconf_iter;
-	const NMPlatformIP4Address *address;
-	const NMPlatformIP4Route *route;
-
-	g_message ("--------- NMIP4Config %p (%s)", self, detail);
+	const NMPlatformIP4Address *addr4;
+	const NMPlatformIP6Address *addr6;
+	const NMPlatformIP4Route *route4;
+	const NMPlatformIP6Route *route6;
+	const NMIP4Config *ip4;
+	const NMIP6Config *ip6;
+	int addr_family = AF_UNSPEC;
+	char addr_family_char = '?';
+	const char *path;
+	gconstpointer ptr;
+	guint i;
 
-	if (self == NULL) {
-		g_message (" (null)");
-		return;
+	if (self) {
+		addr_family = nm_ip_config_get_addr_family (self);
+		addr_family_char = nm_utils_addr_family_to_char (addr_family);
 	}
 
-	str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
-	if (str)
-		g_message ("   path: %s", str);
+	nm_log (level, domain, NULL, NULL,
+	        "---- NMIP%cConfig %p (%s)",
+	        addr_family_char,
+	        self,
+	        detail);
 
-	/* addresses */
-	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address)
-		g_message ("      a: %s", nm_platform_ip4_address_to_string (address, NULL, 0));
-
-	/* nameservers */
-	for (i = 0; i < nm_ip4_config_get_num_nameservers (self); i++) {
-		tmp = nm_ip4_config_get_nameserver (self, i);
-		g_message ("     ns: %s", nm_utils_inet4_ntop (tmp, NULL));
-	}
-
-	/* routes */
-	nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route)
-		g_message ("     rt: %s", nm_platform_ip4_route_to_string (route, NULL, 0));
+	if (!self)
+		return;
 
-	/* domains */
-	for (i = 0; i < nm_ip4_config_get_num_domains (self); i++)
-		g_message (" domain: %s", nm_ip4_config_get_domain (self, i));
+	path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
+	if (path)
+		nm_log (level, domain, NULL, NULL, " path     : %s", path);
 
-	/* dns searches */
-	for (i = 0; i < nm_ip4_config_get_num_searches (self); i++)
-		g_message (" search: %s", nm_ip4_config_get_search (self, i));
+	if (addr_family == AF_INET) {
+		ip4 = NM_IP4_CONFIG (self);
+		nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &addr4) {
+			nm_log (level, domain, NULL, NULL, " address  : %s",
+			        nm_platform_ip4_address_to_string (addr4, NULL, 0));
+		}
+		nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route4) {
+			nm_log (level, domain, NULL, NULL, " route    : %s",
+			        nm_platform_ip4_route_to_string (route4, NULL, 0));
+		}
+	} else {
+		ip6 = NM_IP6_CONFIG (self);
+		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &addr6) {
+			nm_log (level, domain, NULL, NULL, " address  : %s",
+			        nm_platform_ip6_address_to_string (addr6, NULL, 0));
+		}
+		nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route6) {
+			nm_log (level, domain, NULL, NULL, " route    : %s",
+			        nm_platform_ip6_route_to_string (route6, NULL, 0));
+		}
+	}
 
-	/* dns options */
-	for (i = 0; i < nm_ip4_config_get_num_dns_options (self); i++)
-		g_message (" dnsopt: %s", nm_ip4_config_get_dns_option (self, i));
+	for (i = 0; i < nm_ip_config_get_num_nameservers (self); i++) {
+		ptr = nm_ip_config_get_nameserver (self, i);
+		nm_log (level, domain, NULL, NULL, " dns      : %s",
+		        nm_utils_inet_ntop (addr_family, ptr, NULL));
+	}
 
-	g_message (" dnspri: %d", nm_ip4_config_get_dns_priority (self));
+	for (i = 0; i < nm_ip_config_get_num_domains (self); i++)
+		nm_log (level, domain, NULL, NULL, " domain   : %s", nm_ip_config_get_domain (self, i));
 
-	g_message ("    mtu: %"G_GUINT32_FORMAT" (source: %d)", nm_ip4_config_get_mtu (self), (int) nm_ip4_config_get_mtu_source (self));
+	for (i = 0; i < nm_ip_config_get_num_searches (self); i++)
+		nm_log (level, domain, NULL, NULL, " search   : %s", nm_ip_config_get_search (self, i));
 
-	/* NIS */
-	for (i = 0; i < nm_ip4_config_get_num_nis_servers (self); i++) {
-		tmp = nm_ip4_config_get_nis_server (self, i);
-		g_message ("    nis: %s", nm_utils_inet4_ntop (tmp, NULL));
-	}
+	for (i = 0; i < nm_ip_config_get_num_dns_options (self); i++)
+		nm_log (level, domain, NULL, NULL, "dns-option: %s", nm_ip_config_get_dns_option (self, i));
 
-	g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (self) ?: "(none)");
+	nm_log (level, domain, NULL, NULL, " dns-prio : %d", nm_ip_config_get_dns_priority (self));
 
-	/* WINS */
-	for (i = 0; i < nm_ip4_config_get_num_wins (self); i++) {
-		tmp = nm_ip4_config_get_wins (self, i);
-		g_message ("   wins: %s", nm_utils_inet4_ntop (tmp, NULL));
+	if (addr_family == AF_INET) {
+		ip4 = NM_IP4_CONFIG (self);
+		nm_log (level, domain, NULL, NULL,
+		        " mtu      : %"G_GUINT32_FORMAT" (source: %d)",
+		        nm_ip4_config_get_mtu (ip4),
+		        (int) nm_ip4_config_get_mtu_source (ip4));
+		nm_log (level, domain, NULL, NULL, " metered  : %d",
+		        (int) nm_ip4_config_get_metered (ip4));
 	}
-
-	g_message (" mtrd:   %d", (int) nm_ip4_config_get_metered (self));
 }
 
 /*****************************************************************************/
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index d9ac969c..07fb7f12 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -189,12 +189,13 @@ void nm_ip4_config_subtract (NMIP4Config *dst,
                              guint32 default_route_metric_penalty);
 void nm_ip4_config_intersect (NMIP4Config *dst,
                               const NMIP4Config *src,
+                              gboolean intersect_routes,
                               guint32 default_route_metric_penalty);
 NMIP4Config *nm_ip4_config_intersect_alloc (const NMIP4Config *a,
                                             const NMIP4Config *b,
+                                            gboolean intersect_routes,
                                             guint32 default_route_metric_penalty);
 gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes);
-void nm_ip4_config_dump (const NMIP4Config *self, const char *detail);
 
 const NMPObject *nm_ip4_config_best_default_route_get (const NMIP4Config *self);
 const NMPObject *_nm_ip4_config_best_default_route_find (const NMIP4Config *self);
@@ -297,6 +298,11 @@ gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b);
 
 gboolean _nm_ip_config_check_and_add_domain (GPtrArray *array, const char *domain);
 
+void nm_ip_config_dump (const NMIPConfig *self,
+                        const char *detail,
+                        NMLogLevel level,
+                        NMLogDomain domain);
+
 /*****************************************************************************/
 
 #include "nm-ip6-config.h"
@@ -537,11 +543,13 @@ nm_ip_config_best_default_route_get (const NMIPConfig *self)
 static inline void
 nm_ip_config_intersect (NMIPConfig *dst,
                         const NMIPConfig *src,
+                        gboolean intersect_routes,
                         guint32 default_route_metric_penalty)
 {
 	_NM_IP_CONFIG_DISPATCH_SET_OP (, dst, src,
 	                               nm_ip4_config_intersect,
 	                               nm_ip6_config_intersect,
+	                               intersect_routes,
 	                               default_route_metric_penalty);
 }
 
@@ -583,18 +591,21 @@ nm_ip_config_replace (NMIPConfig *dst,
 static inline NMIPConfig *
 nm_ip_config_intersect_alloc (const NMIPConfig *a,
                               const NMIPConfig *b,
+                              gboolean intersect_routes,
                               guint32 default_route_metric_penalty)
 {
 	if (NM_IS_IP4_CONFIG (a)) {
 		nm_assert (NM_IS_IP4_CONFIG (b));
 		return (NMIPConfig *) nm_ip4_config_intersect_alloc ((const NMIP4Config *) a,
 		                                                     (const NMIP4Config *) b,
+		                                                     intersect_routes,
 		                                                     default_route_metric_penalty);
 	} else {
 		nm_assert (NM_IS_IP6_CONFIG (a));
 		nm_assert (NM_IS_IP6_CONFIG (b));
 		return (NMIPConfig *) nm_ip6_config_intersect_alloc ((const NMIP6Config *) a,
 		                                                     (const NMIP6Config *) b,
+		                                                     intersect_routes,
 		                                                     default_route_metric_penalty);
 	}
 }
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 378b76d6..a8c4aecd 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -1084,6 +1084,7 @@ nm_ip6_config_subtract (NMIP6Config *dst,
 static gboolean
 _nm_ip6_config_intersect_helper (NMIP6Config *dst,
                                  const NMIP6Config *src,
+                                 gboolean intersect_routes,
                                  guint32 default_route_metric_penalty,
                                  gboolean update_dst)
 {
@@ -1128,6 +1129,9 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
 	/* ignore nameservers */
 
 	/* routes */
+	if (!intersect_routes)
+		goto skip_routes;
+
 	changed = FALSE;
 	new_best_default_route = NULL;
 	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, dst, &r) {
@@ -1172,6 +1176,7 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
 		result = TRUE;
 	}
 
+skip_routes:
 	/* ignore domains */
 	/* ignore dns searches */
 	/* ignore dns options */
@@ -1194,9 +1199,10 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
 void
 nm_ip6_config_intersect (NMIP6Config *dst,
                          const NMIP6Config *src,
+                         gboolean intersect_routes,
                          guint32 default_route_metric_penalty)
 {
-	_nm_ip6_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE);
+	_nm_ip6_config_intersect_helper (dst, src, intersect_routes, default_route_metric_penalty, TRUE);
 }
 
 /**
@@ -1217,14 +1223,17 @@ nm_ip6_config_intersect (NMIP6Config *dst,
 NMIP6Config *
 nm_ip6_config_intersect_alloc (const NMIP6Config *a,
                                const NMIP6Config *b,
+                               gboolean intersect_routes,
                                guint32 default_route_metric_penalty)
 {
 	NMIP6Config *a_copy;
 
 	if (_nm_ip6_config_intersect_helper ((NMIP6Config *) a, b,
+	                                     intersect_routes,
 	                                     default_route_metric_penalty, FALSE)) {
 		a_copy = nm_ip6_config_clone (a);
-		_nm_ip6_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE);
+		_nm_ip6_config_intersect_helper (a_copy, b, intersect_routes,
+		                                 default_route_metric_penalty, TRUE);
 		return a_copy;
 	} else
 		return NULL;
@@ -1483,53 +1492,6 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
 	return has_relevant_changes || has_minor_changes;
 }
 
-void
-nm_ip6_config_dump (const NMIP6Config *self, const char *detail)
-{
-	const struct in6_addr *tmp;
-	guint32 i;
-	const char *str;
-	NMDedupMultiIter ipconf_iter;
-	const NMPlatformIP6Address *address;
-	const NMPlatformIP6Route *route;
-
-	g_return_if_fail (self != NULL);
-
-	g_message ("--------- NMIP6Config %p (%s)", self, detail);
-
-	str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
-	if (str)
-		g_message ("   path: %s", str);
-
-	/* addresses */
-	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address)
-		g_message ("      a: %s", nm_platform_ip6_address_to_string (address, NULL, 0));
-
-	/* nameservers */
-	for (i = 0; i < nm_ip6_config_get_num_nameservers (self); i++) {
-		tmp = nm_ip6_config_get_nameserver (self, i);
-		g_message ("     ns: %s", nm_utils_inet6_ntop (tmp, NULL));
-	}
-
-	/* routes */
-	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route)
-		g_message ("     rt: %s", nm_platform_ip6_route_to_string (route, NULL, 0));
-
-	/* domains */
-	for (i = 0; i < nm_ip6_config_get_num_domains (self); i++)
-		g_message (" domain: %s", nm_ip6_config_get_domain (self, i));
-
-	/* dns searches */
-	for (i = 0; i < nm_ip6_config_get_num_searches (self); i++)
-		g_message (" search: %s", nm_ip6_config_get_search (self, i));
-
-	/* dns options */
-	for (i = 0; i < nm_ip6_config_get_num_dns_options (self); i++)
-		g_message (" dnsopt: %s", nm_ip6_config_get_dns_option (self, i));
-
-	g_message (" dnspri: %d", nm_ip6_config_get_dns_priority (self));
-}
-
 /*****************************************************************************/
 
 void
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index 425b2049..862f237d 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -130,12 +130,13 @@ void nm_ip6_config_subtract (NMIP6Config *dst,
                              guint32 default_route_metric_penalty);
 void nm_ip6_config_intersect (NMIP6Config *dst,
                               const NMIP6Config *src,
+                              gboolean intersect_routes,
                               guint32 default_route_metric_penalty);
 NMIP6Config *nm_ip6_config_intersect_alloc (const NMIP6Config *a,
                                             const NMIP6Config *b,
+                                            gboolean intersect_routes,
                                             guint32 default_route_metric_penalty);
 gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes);
-void nm_ip6_config_dump (const NMIP6Config *self, const char *detail);
 
 const NMPObject *nm_ip6_config_best_default_route_get (const NMIP6Config *self);
 const NMPObject *_nm_ip6_config_best_default_route_find (const NMIP6Config *self);
diff --git a/src/nm-manager.c b/src/nm-manager.c
index f8be6d37..7598995d 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1632,7 +1632,7 @@ remove_device (NMManager *self,
 				nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_REMOVED);
 				nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
 			}
-		} else if (quitting && nm_config_get_configure_and_quit (priv->config)) {
+		} else if (quitting && nm_config_get_configure_and_quit (priv->config) == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) {
 			nm_device_spawn_iface_helper (device);
 		}
 	}
@@ -3319,6 +3319,49 @@ nm_manager_get_devices (NMManager *manager)
 	return &NM_MANAGER_GET_PRIVATE (manager)->devices_lst_head;
 }
 
+typedef enum {
+	DEVICE_ACTIVATION_PRIO_NONE,
+	DEVICE_ACTIVATION_PRIO_UNMANAGED,
+	DEVICE_ACTIVATION_PRIO_UNAVAILABLE,
+	DEVICE_ACTIVATION_PRIO_DEACTIVATING,
+	DEVICE_ACTIVATION_PRIO_ACTIVATING,
+	DEVICE_ACTIVATION_PRIO_ACTIVATED,
+	DEVICE_ACTIVATION_PRIO_DISCONNECTED,
+
+	_DEVICE_ACTIVATION_PRIO_BEST = DEVICE_ACTIVATION_PRIO_DISCONNECTED,
+} DeviceActivationPrio;
+
+static DeviceActivationPrio
+_device_get_activation_prio (NMDevice *device)
+{
+	if (!nm_device_get_managed (device, TRUE))
+		return DEVICE_ACTIVATION_PRIO_NONE;
+
+	switch (nm_device_get_state (device)) {
+	case NM_DEVICE_STATE_DISCONNECTED:
+		return DEVICE_ACTIVATION_PRIO_DISCONNECTED;
+	case NM_DEVICE_STATE_ACTIVATED:
+		return DEVICE_ACTIVATION_PRIO_ACTIVATED;
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_CONFIG:
+	case NM_DEVICE_STATE_NEED_AUTH:
+	case NM_DEVICE_STATE_IP_CONFIG:
+	case NM_DEVICE_STATE_IP_CHECK:
+	case NM_DEVICE_STATE_SECONDARIES:
+		return DEVICE_ACTIVATION_PRIO_ACTIVATING;
+	case NM_DEVICE_STATE_DEACTIVATING:
+	case NM_DEVICE_STATE_FAILED:
+		return DEVICE_ACTIVATION_PRIO_DEACTIVATING;
+	case NM_DEVICE_STATE_UNAVAILABLE:
+		return DEVICE_ACTIVATION_PRIO_UNAVAILABLE;
+	case NM_DEVICE_STATE_UNKNOWN:
+	case NM_DEVICE_STATE_UNMANAGED:
+		return DEVICE_ACTIVATION_PRIO_UNMANAGED;
+	}
+
+	g_return_val_if_reached (DEVICE_ACTIVATION_PRIO_UNAVAILABLE);
+}
+
 static NMDevice *
 nm_manager_get_best_device_for_connection (NMManager *self,
                                            NMSettingsConnection *sett_conn,
@@ -3332,9 +3375,17 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	NMActiveConnection *ac;
 	NMDevice *ac_device;
 	NMDevice *device;
+	struct {
+		NMDevice *device;
+		DeviceActivationPrio prio;
+	} best = {
+		.device = NULL,
+		.prio = DEVICE_ACTIVATION_PRIO_NONE,
+	};
 	NMDeviceCheckConAvailableFlags flags;
 	gs_unref_ptrarray GPtrArray *all_ac_arr = NULL;
 	gs_free_error GError *local_best = NULL;
+	NMConnectionMultiConnect multi_connect;
 
 	nm_assert (!sett_conn || NM_IS_SETTINGS_CONNECTION (sett_conn));
 	nm_assert (!connection || NM_IS_CONNECTION (connection));
@@ -3344,10 +3395,52 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	if (!connection)
 		connection = nm_settings_connection_get_connection (sett_conn);
 
-	flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE;
+	multi_connect =  _nm_connection_get_multi_connect (connection);
 
-	ac = active_connection_find_by_connection (self, sett_conn, connection, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, &all_ac_arr);
-	if (ac) {
+	if (!for_user_request)
+		flags = NM_DEVICE_CHECK_CON_AVAILABLE_NONE;
+	else {
+		/* if the profile is multi-connect=single, we also consider devices which
+		 * are marked as unmanaged. And explicit user-request shows sufficent user
+		 * intent to make the device managed.
+		 * That is also, because we expect that such profile is suitably tied
+		 * to the intended device. So when an unmanaged device matches, the user's
+		 * intent is clear.
+		 *
+		 * For multi-connect != single devices that is different. The profile
+		 * is not restricted to a particular device.
+		 * For that reason, plain `nmcli connection up "$MULIT_PROFILE"` seems
+		 * less suitable for multi-connect profiles, because the target device is
+		 * left unspecified. Anyway, if a user issues
+		 *
+		 *   $ nmcli device set "$DEVICE" managed no
+		 *   $ nmcli connection up "$MULIT_PROFILE"
+		 *
+		 * then it is reasonable for multi-connect profiles to not consider
+		 * the device a suitable candidate.
+		 *
+		 * This may be seen inconsistent, but I think that it makes a lot of
+		 * sense. Also note that "connection.multi-connect" work quite differently
+		 * in aspects like activation. E.g. `nmcli connection up` of multi-connect
+		 * "single" profile, will deactivate the profile if it is active already.
+		 * That is different from multi-connect profiles, where it will aim to
+		 * activate the profile one more time on an hitherto disconnected device.
+		 */
+		if (multi_connect == NM_CONNECTION_MULTI_CONNECT_SINGLE)
+			flags = NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST;
+		else
+			flags = NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST & ~_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED;
+	}
+
+	if (   multi_connect == NM_CONNECTION_MULTI_CONNECT_SINGLE
+	    && (ac = active_connection_find_by_connection (self, sett_conn, connection, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, &all_ac_arr))) {
+		/* if we have a profile which may activate on only one device (multi-connect single), then
+		 * we prefer the device on which the profile is already active. It means to reactivate
+		 * the profile on the same device.
+		 *
+		 * If the profile can be activated on multiple devices, we don't do this. In fact, the
+		 * check below for the DeviceActivationPrio will prefer devices which are not already
+		 * activated (with this or another) profile. */
 
 		ac_device = nm_active_connection_get_device (ac);
 		if (   ac_device
@@ -3418,17 +3511,48 @@ found_better:
 	/* Pick the first device that's compatible with the connection. */
 	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 		GError *local = NULL;
+		DeviceActivationPrio prio;
 
 		if (   unavailable_devices
 		    && g_hash_table_contains (unavailable_devices, device))
 			continue;
 
+		/* determine the priority of this device. Currently this priority is independent
+		 * of the profile (connection) and the device's details (aside the state).
+		 *
+		 * Maybe nm_device_check_connection_available() should instead return a priority,
+		 * as it has more information available.
+		 *
+		 * For example, if you have multiple Wi-Fi devices, currently a user-request would
+		 * also select the device if the AP is not visible. Optimally, if one of the two
+		 * devices sees the AP and the other one doesn't, the former would be preferred.
+		 * For that, the priority would need to be determined by nm_device_check_connection_available(). */
+		prio = _device_get_activation_prio (device);
+		if (   prio <= best.prio
+		    && best.device) {
+			/* we already have a matching device with a better priority. This candidate
+			 * cannot be better. Skip the check.
+			 *
+			 * Also note, that below we collect the best error message @local_best.
+			 * Since we already have best.device, the error message does not matter
+			 * either, and we can skip nm_device_check_connection_available() altogether. */
+			continue;
+		}
+
 		if (nm_device_check_connection_available (device,
 		                                          connection,
 		                                          flags,
 		                                          NULL,
-		                                          error ? &local : NULL))
-			return device;
+		                                          error ? &local : NULL)) {
+			if (prio == _DEVICE_ACTIVATION_PRIO_BEST) {
+				/* this device already has the best priority. It cannot get better
+				 * and finish the search. */
+				return device;
+			}
+			best.prio = prio;
+			best.device = device;
+			continue;
+		}
 
 		if (error) {
 			gboolean reset_error;
@@ -3455,6 +3579,9 @@ found_better:
 		}
 	}
 
+	if (best.device)
+		return best.device;
+
 	if (error) {
 		if (local_best)
 			g_propagate_error (error, g_steal_pointer (&local_best));
@@ -6101,6 +6228,8 @@ nm_manager_write_device_state (NMManager *self, NMDevice *device)
 	guint32 route_metric_default_aspired;
 	guint32 route_metric_default_effective;
 	int nm_owned;
+	NMDhcp4Config *dhcp4_config;
+	const char *root_path = NULL;
 
 	ifindex = nm_device_get_ip_ifindex (device);
 	if (ifindex <= 0)
@@ -6115,9 +6244,10 @@ nm_manager_write_device_state (NMManager *self, NMDevice *device)
 
 	managed = nm_device_get_managed (device, FALSE);
 	if (managed) {
-		NMSettingsConnection *sett_conn;
+		NMSettingsConnection *sett_conn = NULL;
 
-		sett_conn = nm_device_get_settings_connection (device);
+		if (nm_device_get_state (device) <= NM_DEVICE_STATE_ACTIVATED)
+			sett_conn = nm_device_get_settings_connection (device);
 		if (sett_conn)
 			uuid = nm_settings_connection_get_uuid (sett_conn);
 		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
@@ -6135,13 +6265,18 @@ nm_manager_write_device_state (NMManager *self, NMDevice *device)
 	route_metric_default_effective = _device_route_metric_get (self, ifindex, NM_DEVICE_TYPE_UNKNOWN,
 	                                                           TRUE, &route_metric_default_aspired);
 
+	dhcp4_config = nm_device_get_dhcp4_config (device);
+	if (dhcp4_config)
+		root_path = nm_dhcp4_config_get_option (dhcp4_config, "root_path");
+
 	return nm_config_device_state_write (ifindex,
 	                                     managed_type,
 	                                     perm_hw_addr_fake,
 	                                     uuid,
 	                                     nm_owned,
 	                                     route_metric_default_aspired,
-	                                     route_metric_default_effective);
+	                                     route_metric_default_effective,
+	                                     root_path);
 }
 
 void
diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h
index 1fccddb9..d5df7ab9 100644
--- a/src/platform/nm-netlink.h
+++ b/src/platform/nm-netlink.h
@@ -286,7 +286,7 @@ struct nlattr *nla_find (const struct nlattr *head, int len, int attrtype);
 static inline int
 nla_ok (const struct nlattr *nla, int remaining)
 {
-	return remaining >= sizeof(*nla) &&
+	return remaining >= (int) sizeof(*nla) &&
 	       nla->nla_len >= sizeof(*nla) &&
 	       nla->nla_len <= remaining;
 }
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 1b2edc83..4c25d0e9 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -2510,6 +2510,8 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *self,
 
 	if (flush_to_disk == FALSE)
 		return;
+	if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
+		return;
 
 	/* Save timestamp to timestamps database file */
 	timestamps_file = g_key_file_new ();
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1
deleted file mode 100755
index 54f02da6..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = STATIC
-iface.ipaddress = 192.168.32.72
-iface.subnet_mask = 255.255.252.0
-iface.gateway = 192.168.35.254
-iface.primary_dns = 10000.500.250.1
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-EOF
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2
deleted file mode 100755
index ebd7a9ca..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = STATIC
-iface.ipaddress = 192.168.32.72
-iface.subnet_mask = 255.255.252.0
-iface.gateway = 192.168.35.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = blah.foo.bar.baz
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-EOF
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry
deleted file mode 100755
index 4e326048..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = STATIC
-iface.ipaddress 192.168.32.72
-iface.subnet_mask = 255.255.252.0
-iface.gateway = 192.168.35.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway
deleted file mode 100755
index 5390a6c3..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = STATIC
-iface.ipaddress = 192.168.32.72
-iface.subnet_mask = 255.255.252.0
-iface.gateway = bb.cc.dd.ee
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-EOF
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr
deleted file mode 100755
index b41cd1f1..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = STATIC
-iface.ipaddress = aa.bb.cc.dd
-iface.subnet_mask = 255.255.252.0
-iface.gateway = 192.168.35.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-EOF
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record
deleted file mode 100755
index 22b34e6f..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = DHCP
-iface.gateway = 10.16.52.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-EOF
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp
deleted file mode 100755
index 556b0586..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = DHCP
-iface.gateway = 10.16.52.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f1
-iface.bootproto = DHCP
-iface.gateway = 10.16.52.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth1
-node.name = iqn.1.2008-11.com.blahblah:iscsi1
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-EOF
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-static b/src/settings/plugins/ibft/tests/iscsiadm-test-static
deleted file mode 100755
index 51711480..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-static
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = STATIC
-iface.ipaddress = 192.168.32.72
-iface.subnet_mask = 255.255.252.0
-iface.gateway = 192.168.35.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth0
-node.name = iqn.0.2008-11.com.blahblah:iscsi0
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-# BEGIN RECORD
-iface.initiatorname = iqn.pjones6
-iface.hwaddress = 00:33:21:98:b9:f1
-iface.bootproto = DHCP
-iface.gateway = 10.16.52.254
-iface.primary_dns = 10.16.255.2
-iface.secondary_dns = 10.16.255.3
-iface.vlan_id = 0
-iface.net_ifacename = eth1
-node.name = iqn.1.2008-11.com.blahblah:iscsi1
-node.conn[0].address = 10.16.52.16
-node.conn[0].port = 3260
-node.boot_lun = 00000000
-# END RECORD
-EOF
-
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan
deleted file mode 100755
index 59b80bd0..00000000
--- a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-
-cat << EOF
-# BEGIN RECORD 6.2.0.873-21
-iface.initiatorname = iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0
-iface.transport_name = tcp
-iface.hwaddress = 00:33:21:98:b9:f0
-iface.bootproto = STATIC
-iface.ipaddress = 192.168.6.200
-iface.subnet_mask = 255.255.255.0
-iface.vlan_id = 123
-iface.net_ifacename = eth0
-node.name = iqn.2003-01.org.x:disk1
-node.conn[0].address = 192.168.6.32
-node.conn[0].port = 3260
-node.boot_lun = 01000000
-# END RECORD
-EOF
-
diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build
index e4bce09b..e84ae80c 100644
--- a/src/settings/plugins/ifcfg-rh/meson.build
+++ b/src/settings/plugins/ifcfg-rh/meson.build
@@ -63,6 +63,15 @@ check-local-symbols-settings-ifcfg-rh: src/settings/plugins/ifcfg-rh/libnm-setti
   $(call check_so_symbols,$(builddir)/src/settings/plugins/ifcfg-rh/.libs/libnm-settings-plugin-ifcfg-rh.so)
 '''
 
+install_data(
+  ['nm-ifup', 'nm-ifdown'],
+  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/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 0c0dd64e..472bb8a6 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -9054,7 +9054,7 @@ test_team_reread_slave (void)
 	        "id=142\n"
 	        "ingress-priority-map=\n"
 	        "parent=enp31s0f1\n"
-	        , "/test_team_reread_slave", NULL);
+	        , "/test_team_reread_slave");
 
 	/* to double-check keyfile syntax, re-create the connection by hand. */
 	connection_2 = nmtst_create_minimal_connection ("team-slave-enp31s0f1-142", "74f435bb-ede4-415a-9d48-f580b60eba04", NM_SETTING_VLAN_SETTING_NAME, &s_con);
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
index d926829f..73ecc2f9 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
@@ -126,17 +126,17 @@ _recursive_ifparser (if_parser *parser, const char *eni_file, int quiet)
 	/* Check if interfaces file exists and open it */
 	if (!g_file_test (eni_file, G_FILE_TEST_EXISTS)) {
 		if (!quiet)
-			_LOGW ("interfaces file %s doesn't exist\n", eni_file);
+			_LOGW ("interfaces file %s doesn't exist", eni_file);
 		return;
 	}
 	inp = fopen (eni_file, "re");
 	if (inp == NULL) {
 		if (!quiet)
-			_LOGW ("Can't open %s\n", eni_file);
+			_LOGW ("Can't open %s", eni_file);
 		return;
 	}
 	if (!quiet)
-		_LOGI ("      interface-parser: parsing file %s\n", eni_file);
+		_LOGI ("      interface-parser: parsing file %s", eni_file);
 
 	while (!feof (inp)) {
 		char *token[128]; /* 255 chars can only be split into 127 tokens */
@@ -154,7 +154,7 @@ _recursive_ifparser (if_parser *parser, const char *eni_file, int quiet)
 		if (!feof (inp) && len > 0 &&  line[len-1] != '\n') {
 			if (!skip_long_line) {
 				if (!quiet)
-					_LOGW ("Skipping over-long-line '%s...'\n", line);
+					_LOGW ("Skipping over-long-line '%s...'", line);
 			}
 			skip_long_line = 1;
 			continue;
@@ -192,7 +192,7 @@ _recursive_ifparser (if_parser *parser, const char *eni_file, int quiet)
 
 		if (toknum < 2) {
 			if (!quiet) {
-				_LOGW ("Can't parse interface line '%s'\n",
+				_LOGW ("Can't parse interface line '%s'",
 				       join_values_with_spaces (value, token));
 			}
 			skip_to_block = 1;
@@ -207,7 +207,7 @@ _recursive_ifparser (if_parser *parser, const char *eni_file, int quiet)
 		if (nm_streq (token[0], "iface")) {
 			if (toknum < 4) {
 				if (!quiet) {
-					_LOGW ("Can't parse iface line '%s'\n",
+					_LOGW ("Can't parse iface line '%s'",
 					       join_values_with_spaces (value, token));
 				}
 				continue;
@@ -254,7 +254,7 @@ _recursive_ifparser (if_parser *parser, const char *eni_file, int quiet)
 		else {
 			if (skip_to_block) {
 				if (!quiet) {
-					_LOGW ("ignoring out-of-block data '%s'\n",
+					_LOGW ("ignoring out-of-block data '%s'",
 					       join_values_with_spaces (value, token));
 				}
 			} else
@@ -264,7 +264,7 @@ _recursive_ifparser (if_parser *parser, const char *eni_file, int quiet)
 	fclose (inp);
 
 	if (!quiet)
-		_LOGI ("      interface-parser: finished parsing file %s\n", eni_file);
+		_LOGI ("      interface-parser: finished parsing file %s", eni_file);
 }
 
 static void
@@ -283,12 +283,12 @@ _ifparser_source (if_parser *parser, const char *path, const char *en_dir, int q
 		abs_path = g_build_filename (en_dir, path, NULL);
 
 	if (!quiet)
-		_LOGI ("      interface-parser: source line includes interfaces file(s) %s\n", abs_path);
+		_LOGI ("      interface-parser: source line includes interfaces file(s) %s", abs_path);
 
 	/* ifupdown uses WRDE_NOCMD for wordexp. */
 	if (wordexp (abs_path, &we, WRDE_NOCMD)) {
 		if (!quiet)
-			_LOGW ("word expansion for %s failed\n", abs_path);
+			_LOGW ("word expansion for %s failed", abs_path);
 	} else {
 		for (i = 0; i < we.we_wordc; i++) {
 			if (dir) {
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index e96350c0..82ee1c4a 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -144,7 +144,7 @@ compare_expected_to_ifparser (if_parser *parser, Expected *e)
 
 		g_assert (b->type && n->type);
 		g_assert_cmpstr (b->type, ==, n->type);
-		g_assert (b->name && n->name);
+		g_assert (b->name);
 		g_assert_cmpstr (b->name, ==, n->name);
 
 		g_assert_cmpint (g_slist_length (b->keys), ==, ifparser_get_num_info (n));
@@ -153,7 +153,7 @@ compare_expected_to_ifparser (if_parser *parser, Expected *e)
 		c_list_for_each_entry (m, &n->data_lst_head, data_lst) {
 			ExpectedKey *k = kiter->data;
 
-			g_assert (k->key && m->key);
+			g_assert (k->key);
 			g_assert_cmpstr (k->key, ==, m->key);
 			g_assert (k->data && m->data);
 			g_assert_cmpstr (k->data, ==, m->data);
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
index 64e94b26..7511f206 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c
@@ -66,6 +66,7 @@ commit_changes (NMSettingsConnection *connection,
 	nm_assert (!out_logmsg_change || !*out_logmsg_change);
 
 	if (!nms_keyfile_writer_connection (new_connection,
+	                                    TRUE,
 	                                    nm_settings_connection_get_filename (connection),
 	                                    NM_FLAGS_ALL (commit_reason,   NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION
 	                                                                 | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED),
@@ -123,6 +124,7 @@ nms_keyfile_connection_init (NMSKeyfileConnection *connection)
 NMSKeyfileConnection *
 nms_keyfile_connection_new (NMConnection *source,
                             const char *full_path,
+                            const char *profile_dir,
                             GError **error)
 {
 	GObject *object;
@@ -130,13 +132,15 @@ nms_keyfile_connection_new (NMConnection *source,
 	const char *uuid;
 	gboolean update_unsaved = TRUE;
 
-	g_assert (source || full_path);
+	nm_assert (source || full_path);
+	nm_assert (!full_path || full_path[0] == '/');
+	nm_assert (!profile_dir || profile_dir[0] == '/');
 
 	/* If we're given a connection already, prefer that instead of re-reading */
 	if (source)
 		tmp = g_object_ref (source);
 	else {
-		tmp = nms_keyfile_reader_from_file (full_path, error);
+		tmp = nms_keyfile_reader_from_file (full_path, profile_dir, error);
 		if (!tmp)
 			return NULL;
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.h b/src/settings/plugins/keyfile/nms-keyfile-connection.h
index f96d7590..0773ced0 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-connection.h
@@ -37,7 +37,8 @@ typedef struct _NMSKeyfileConnectionClass NMSKeyfileConnectionClass;
 GType nms_keyfile_connection_get_type (void);
 
 NMSKeyfileConnection *nms_keyfile_connection_new (NMConnection *source,
-                                                  const char *filename,
+                                                  const char *full_path,
+                                                  const char *profile_dir,
                                                   GError **error);
 
 #endif /* __NMS_KEYFILE_CONNECTION_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index 89b89467..346b78c0 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -171,6 +171,7 @@ update_connection (NMSKeyfilePlugin *self,
 	NMSKeyfileConnection *connection_by_uuid;
 	GError *local = NULL;
 	const char *uuid;
+	int dir_len;
 
 	g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL);
 	g_return_val_if_fail (full_path || source, NULL);
@@ -178,7 +179,23 @@ update_connection (NMSKeyfilePlugin *self,
 	if (full_path)
 		_LOGD ("loading from file \"%s\"...", full_path);
 
-	connection_new = nms_keyfile_connection_new (source, full_path, &local);
+	if (g_str_has_prefix (full_path, nms_keyfile_utils_get_path ())) {
+		dir_len = strlen (nms_keyfile_utils_get_path ());
+	} else if (g_str_has_prefix (full_path, NM_CONFIG_KEYFILE_PATH_IN_MEMORY)) {
+		dir_len = NM_STRLEN (NM_CONFIG_KEYFILE_PATH_IN_MEMORY);
+	} else {
+		/* Just make sure the file name is not going go pass the following check. */
+		dir_len = strlen (full_path);
+	}
+
+	if (   full_path[dir_len] != '/'
+	    || strchr (full_path + dir_len + 1, '/') != NULL) {
+		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
+		                     "File not in recognized system-connections directory");
+		return FALSE;
+	}
+
+	connection_new = nms_keyfile_connection_new (source, full_path, nms_keyfile_utils_get_path (), &local);
 	if (!connection_new) {
 		/* Error; remove the connection */
 		if (source)
@@ -305,7 +322,7 @@ dir_changed (GFileMonitor *monitor,
 	gboolean exists;
 
 	full_path = g_file_get_path (file);
-	if (nms_keyfile_utils_should_ignore_file (full_path)) {
+	if (nms_keyfile_utils_should_ignore_file (full_path, FALSE)) {
 		g_free (full_path);
 		return;
 	}
@@ -411,13 +428,35 @@ _sort_paths (const char **f1, const char **f2, GHashTable *paths)
 }
 
 static void
+_read_dir (GPtrArray *filenames,
+           const char *path,
+           gboolean require_extension)
+{
+	GDir *dir;
+	const char *item;
+	GError *error = NULL;
+
+	dir = g_dir_open (path, 0, &error);
+	if (!dir) {
+		_LOGD ("cannot read directory '%s': %s", path, error->message);
+		g_clear_error (&error);
+		return;
+	}
+
+	while ((item = g_dir_read_name (dir))) {
+		if (nms_keyfile_utils_should_ignore_file (item, require_extension))
+			continue;
+		g_ptr_array_add (filenames, g_build_filename (path, item, NULL));
+	}
+	g_dir_close (dir);
+}
+
+
+static void
 read_connections (NMSettingsPlugin *config)
 {
 	NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config);
 	NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
-	GDir *dir;
-	GError *error = NULL;
-	const char *item;
 	GHashTable *alive_connections;
 	GHashTableIter iter;
 	NMSKeyfileConnection *connection;
@@ -426,24 +465,12 @@ read_connections (NMSettingsPlugin *config)
 	GPtrArray *filenames;
 	GHashTable *paths;
 
-	dir = g_dir_open (nms_keyfile_utils_get_path (), 0, &error);
-	if (!dir) {
-		_LOGW ("cannot read directory '%s': %s",
-		             nms_keyfile_utils_get_path (),
-		             error->message);
-		g_clear_error (&error);
-		return;
-	}
+	filenames = g_ptr_array_new_with_free_func (g_free);
 
-	alive_connections = g_hash_table_new (nm_direct_hash, NULL);
+	_read_dir (filenames, NM_CONFIG_KEYFILE_PATH_IN_MEMORY, TRUE);
+	_read_dir (filenames, nms_keyfile_utils_get_path (), FALSE);
 
-	filenames = g_ptr_array_new_with_free_func (g_free);
-	while ((item = g_dir_read_name (dir))) {
-		if (nms_keyfile_utils_should_ignore_file (item))
-			continue;
-		g_ptr_array_add (filenames, g_build_filename (nms_keyfile_utils_get_path (), item, NULL));
-	}
-	g_dir_close (dir);
+	alive_connections = g_hash_table_new (nm_direct_hash, NULL);
 
 	/* While reloading, we don't replace connections that we already loaded while
 	 * iterating over the files.
@@ -496,19 +523,61 @@ get_connections (NMSettingsPlugin *config)
 }
 
 static gboolean
+_file_is_in_path (const char *abs_filename,
+                  const char *abs_path)
+{
+	gsize l;
+
+	/* FIXME: ensure that both paths are at least normalized (coalescing ".",
+	 * duplicate '/', and trailing '/'). */
+
+	nm_assert (abs_filename && abs_filename[0] == '/');
+	nm_assert (abs_path && abs_path[0] == '/');
+
+	l = strlen (abs_path);
+	if (strncmp (abs_filename, abs_path, l) != 0)
+		return FALSE;
+
+	abs_filename += l;
+	while (abs_filename[0] == '/')
+		abs_filename++;
+
+	if (!abs_filename[0])
+		return FALSE;
+
+	if (strchr (abs_filename, '/'))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
 load_connection (NMSettingsPlugin *config,
                  const char *filename)
 {
 	NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN ((NMSKeyfilePlugin *) config);
 	NMSKeyfileConnection *connection;
-	int dir_len = strlen (nms_keyfile_utils_get_path ());
+	gboolean require_extension;
 
-	if (   strncmp (filename, nms_keyfile_utils_get_path (), dir_len) != 0
-	    || filename[dir_len] != '/'
-	    || strchr (filename + dir_len + 1, '/') != NULL)
+	/* the test whether to require a file extension tries to figure out whether
+	 * the provided filename is inside /etc or /run.
+	 *
+	 * However, on Posix a filename just resolves to an Inode, and there can
+	 * be any kind of paths that point to the same Inode. It's not generally possible
+	 * to check for that (unless, we would stat all files in the target directory
+	 * and see whether their inode matches).
+	 *
+	 * So, when loading the file do something simpler: require that the path
+	 * starts with the well-known prefix. This rejects symlinks or hard links
+	 * which would actually also point to the same file. */
+	if (_file_is_in_path (filename, nms_keyfile_utils_get_path ()))
+		require_extension = FALSE;
+	else if (_file_is_in_path (filename, NM_CONFIG_KEYFILE_PATH_IN_MEMORY))
+		require_extension = TRUE;
+	else
 		return FALSE;
 
-	if (nms_keyfile_utils_should_ignore_file (filename + dir_len + 1))
+	if (nms_keyfile_utils_should_ignore_file (filename, require_extension))
 		return FALSE;
 
 	connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL);
@@ -532,16 +601,16 @@ add_connection (NMSettingsPlugin *config,
 	gs_free char *path = NULL;
 	gs_unref_object NMConnection *reread = NULL;
 
-	if (save_to_disk) {
-		if (!nms_keyfile_writer_connection (connection,
-		                                    NULL,
-		                                    FALSE,
-		                                    &path,
-		                                    &reread,
-		                                    NULL,
-		                                    error))
-			return NULL;
-	}
+	if (!nms_keyfile_writer_connection (connection,
+	                                    save_to_disk,
+	                                    NULL,
+	                                    FALSE,
+	                                    &path,
+	                                    &reread,
+	                                    NULL,
+	                                    error))
+		return NULL;
+
 	return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error));
 }
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 71578686..580a857a 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -28,6 +28,7 @@
 #include "nm-keyfile-internal.h"
 
 #include "NetworkManagerUtils.h"
+#include "nms-keyfile-utils.h"
 
 /*****************************************************************************/
 
@@ -102,51 +103,85 @@ _handler_read (GKeyFile *keyfile,
 NMConnection *
 nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
                                  const char *filename,
+                                 const char *base_dir,
+                                 const char *profile_dir,
                                  gboolean verbose,
                                  GError **error)
 {
+	NMConnection *connection;
 	HandlerReadData data = {
 		.verbose = verbose,
 	};
+	gs_free char *base_dir_free = NULL;
+	gs_free char *profile_filename_free = NULL;
+	gs_free char *filename_id = NULL;
+	const char *profile_filename = NULL;
+
+	nm_assert (filename && filename[0]);
+	nm_assert (!base_dir || base_dir[0] == '/');
+	nm_assert (!profile_dir || profile_dir[0] == '/');
+
+	if (base_dir)
+		nm_assert (!strchr (filename, '/'));
+	else {
+		const char *s;
+
+		nm_assert (filename[0] == '/');
+
+		/* @base_dir may be NULL, in which case @filename must be an absolute path,
+		 * and the directory is taken as the @base_dir. */
+		s = strrchr (filename, '/');
+		base_dir = nm_strndup_a (255, filename, s - filename, &base_dir_free);
+		if (   !profile_dir
+		    || nm_streq (base_dir, profile_dir))
+			profile_filename = filename;
+		filename = &s[1];
+	}
+
+	connection = nm_keyfile_read (key_file, base_dir, _handler_read, &data, error);
+	if (!connection)
+		return NULL;
+
+	if (g_str_has_suffix (filename, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)) {
+		gsize l = strlen (filename);
+
+		if (l > NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION))
+			filename_id = g_strndup (filename, l - NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION));
+	}
+
+	nm_keyfile_read_ensure_id (connection, filename_id ?: filename);
 
-	return nm_keyfile_read (key_file, filename, NULL, _handler_read, &data, error);
+	if (!profile_filename) {
+		profile_filename_free = g_build_filename (profile_dir ?: base_dir, filename, NULL);
+		profile_filename = profile_filename_free;
+	}
+	nm_keyfile_read_ensure_uuid (connection, profile_filename);
+
+	return connection;
 }
 
 NMConnection *
-nms_keyfile_reader_from_file (const char *filename, GError **error)
+nms_keyfile_reader_from_file (const char *full_filename,
+                              const char *profile_dir,
+                              GError **error)
 {
 	gs_unref_keyfile GKeyFile *key_file = NULL;
-	struct stat statbuf;
 	NMConnection *connection = NULL;
 	GError *verify_error = NULL;
 
-	if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) {
-		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		                     "File did not exist or was not a regular file");
-		return NULL;
-	}
+	nm_assert (full_filename && full_filename[0] == '/');
+	nm_assert (!profile_dir || profile_dir[0] == '/');
 
-	if (!NM_FLAGS_HAS (nm_utils_get_testing (), NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK)) {
-		if (statbuf.st_mode & 0077) {
-			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "File permissions (%o) were insecure",
-			             statbuf.st_mode);
-			return NULL;
-		}
-
-		if (statbuf.st_uid != 0) {
-			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "File owner (%o) is insecure",
-			             statbuf.st_mode);
-			return NULL;
-		}
-	}
+	if (!nms_keyfile_utils_check_file_permissions (full_filename,
+	                                               NULL,
+	                                               error))
+		return NULL;
 
 	key_file = g_key_file_new ();
-	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
+	if (!g_key_file_load_from_file (key_file, full_filename, G_KEY_FILE_NONE, error))
 		return NULL;
 
-	connection = nms_keyfile_reader_from_keyfile (key_file, filename, TRUE, error);
+	connection = nms_keyfile_reader_from_keyfile (key_file, full_filename, NULL, profile_dir, TRUE, error);
 	if (!connection)
 		return NULL;
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h
index b60c1e69..c0fb06d1 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h
@@ -26,9 +26,13 @@
 
 NMConnection *nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
                                                const char *filename,
+                                               const char *base_dir,
+                                               const char *profile_dir,
                                                gboolean verbose,
                                                GError **error);
 
-NMConnection *nms_keyfile_reader_from_file (const char *filename, GError **error);
+NMConnection *nms_keyfile_reader_from_file (const char *full_filename,
+                                            const char *profile_dir,
+                                            GError **error);
 
 #endif /* __NMS_KEYFILE_READER_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c
index 03f06670..c3bfcdee 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -24,6 +24,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
@@ -55,18 +56,11 @@ check_mkstemp_suffix (const char *path)
 }
 
 static gboolean
-check_prefix (const char *base, const char *tag)
+check_prefix_dot (const char *base)
 {
-	int len, tag_len;
+	nm_assert (base && base[0]);
 
-	g_return_val_if_fail (base != NULL, TRUE);
-	g_return_val_if_fail (tag != NULL, TRUE);
-
-	len = strlen (base);
-	tag_len = strlen (tag);
-	if ((len > tag_len) && !g_ascii_strncasecmp (base, tag, tag_len))
-		return TRUE;
-	return FALSE;
+	return base[0] == '.';
 }
 
 static gboolean
@@ -90,7 +84,7 @@ check_suffix (const char *base, const char *tag)
 #define DER_TAG ".der"
 
 gboolean
-nms_keyfile_utils_should_ignore_file (const char *filename)
+nms_keyfile_utils_should_ignore_file (const char *filename, gboolean require_extension)
 {
 	gs_free char *base = NULL;
 
@@ -101,7 +95,7 @@ nms_keyfile_utils_should_ignore_file (const char *filename)
 
 	/* Ignore hidden and backup files */
 	/* should_ignore_file() must mirror escape_filename() */
-	if (check_prefix (base, ".") || check_suffix (base, "~"))
+	if (check_prefix_dot (base) || check_suffix (base, "~"))
 		return TRUE;
 	/* Ignore temporary files */
 	if (check_mkstemp_suffix (base))
@@ -110,18 +104,87 @@ nms_keyfile_utils_should_ignore_file (const char *filename)
 	if (check_suffix (base, PEM_TAG) || check_suffix (base, DER_TAG))
 		return TRUE;
 
+	if (require_extension) {
+		gsize l = strlen (base);
+
+		if (   l <= NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)
+		    || !g_str_has_suffix (base, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION))
+			return TRUE;
+	}
+
 	return FALSE;
 }
 
+/*****************************************************************************/
+
+gboolean
+nms_keyfile_utils_check_file_permissions_stat (const struct stat *st,
+                                               GError **error)
+{
+	g_return_val_if_fail (st, FALSE);
+
+	if (!S_ISREG (st->st_mode)) {
+		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		                     "file is not a regular file");
+		return FALSE;
+	}
+
+	if (!NM_FLAGS_HAS (nm_utils_get_testing (), NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK)) {
+		if (st->st_uid != 0) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "File owner (%lld) is insecure",
+			             (long long) st->st_uid);
+			return FALSE;
+		}
+
+		if (st->st_mode & 0077) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "File permissions (%03o) are insecure",
+			             st->st_mode);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+gboolean
+nms_keyfile_utils_check_file_permissions (const char *filename,
+                                          struct stat *out_st,
+                                          GError **error)
+{
+	struct stat st;
+	int errsv;
+
+	g_return_val_if_fail (filename && filename[0] == '/', FALSE);
+
+	if (stat (filename, &st) != 0) {
+		errsv = errno;
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "cannot access file: %s", g_strerror (errsv));
+		return FALSE;
+	}
+
+	if (!nms_keyfile_utils_check_file_permissions_stat (&st, error))
+		return FALSE;
+
+	NM_SET_OUT (out_st, st);
+	return TRUE;
+}
+
+/*****************************************************************************/
+
 char *
-nms_keyfile_utils_escape_filename (const char *filename)
+nms_keyfile_utils_escape_filename (const char *filename,
+                                   gboolean with_extension)
 {
 	GString *str;
 	const char *f = filename;
-	const char ESCAPE_CHAR = '*';
-
 	/* keyfile used to escape with '*', do not change that behavior.
-	 * But for newly added escapings, use '_' instead. */
+	 *
+	 * But for newly added escapings, use '_' instead.
+	 * Also, @with_extension is new-style. */
+	const char ESCAPE_CHAR = with_extension ? '_' : '*';
 	const char ESCAPE_CHAR2 = '_';
 
 	g_return_val_if_fail (filename && filename[0], NULL);
@@ -138,7 +201,7 @@ nms_keyfile_utils_escape_filename (const char *filename)
 
 	/* escape_filename() must avoid anything that should_ignore_file() would reject.
 	 * We can escape here more aggressivly then what we would read back. */
-	if (check_prefix (str->str, "."))
+	if (check_prefix_dot (str->str))
 		str->str[0] = ESCAPE_CHAR2;
 	if (check_suffix (str->str, "~"))
 		str->str[str->len - 1] = ESCAPE_CHAR2;
@@ -147,6 +210,9 @@ nms_keyfile_utils_escape_filename (const char *filename)
 	    || check_suffix (str->str, DER_TAG))
 		g_string_append_c (str, ESCAPE_CHAR2);
 
+	if (with_extension)
+		g_string_append (str, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION);
+
 	return g_string_free (str, FALSE);;
 }
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h
index d5ddb8d5..297dd4ea 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h
@@ -23,16 +23,28 @@
 
 #include "NetworkManagerUtils.h"
 
+#define NM_CONFIG_KEYFILE_PATH_IN_MEMORY NMRUNDIR "/system-connections"
+
+#define NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION      ".nmconnection"
+
 #define NMS_KEYFILE_CONNECTION_LOG_PATH(path)  ((path) ?: "in-memory")
 #define NMS_KEYFILE_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
 #define NMS_KEYFILE_CONNECTION_LOG_ARG(con)    NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con))
 #define NMS_KEYFILE_CONNECTION_LOG_FMTD        "%s (%s,\"%s\",%p)"
 #define NMS_KEYFILE_CONNECTION_LOG_ARGD(con)   NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con)
 
-gboolean nms_keyfile_utils_should_ignore_file (const char *filename);
+gboolean nms_keyfile_utils_should_ignore_file (const char *filename, gboolean require_extension);
 
-char *nms_keyfile_utils_escape_filename (const char *filename);
+char *nms_keyfile_utils_escape_filename (const char *filename, gboolean with_extension);
 
 const char *nms_keyfile_utils_get_path (void);
 
+struct stat;
+gboolean nms_keyfile_utils_check_file_permissions_stat (const struct stat *st,
+                                                        GError **error);
+
+gboolean nms_keyfile_utils_check_file_permissions (const char *filename,
+                                                   struct stat *out_st,
+                                                   GError **error);
+
 #endif /* __NMS_KEYFILE_UTILS_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index 194b97d4..df26ea60 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -172,6 +172,8 @@ _handler_write (NMConnection *connection,
 static gboolean
 _internal_write_connection (NMConnection *connection,
                             const char *keyfile_dir,
+                            const char *profile_dir,
+                            gboolean with_extension,
                             uid_t owner_uid,
                             pid_t owner_grp,
                             const char *existing_path,
@@ -189,10 +191,14 @@ _internal_write_connection (NMConnection *connection,
 	WriteInfo info = { 0 };
 	GError *local_err = NULL;
 	int errsv;
+	gboolean rename = force_rename;
 
 	g_return_val_if_fail (!out_path || !*out_path, FALSE);
 	g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE);
 
+	if (existing_path && !g_str_has_prefix (existing_path, keyfile_dir))
+		rename = TRUE;
+
 	switch (_nm_connection_verify (connection, error)) {
 	case NM_SETTING_VERIFY_NORMALIZABLE:
 		nm_assert_not_reached ();
@@ -221,10 +227,10 @@ _internal_write_connection (NMConnection *connection,
 	/* If we have existing file path, use it. Else generate one from
 	 * connection's ID.
 	 */
-	if (existing_path != NULL && !force_rename) {
+	if (existing_path != NULL && !rename) {
 		path = g_strdup (existing_path);
 	} else {
-		char *filename_escaped = nms_keyfile_utils_escape_filename (id);
+		char *filename_escaped = nms_keyfile_utils_escape_filename (id, with_extension);
 
 		path = g_build_filename (keyfile_dir, filename_escaped, NULL);
 		g_free (filename_escaped);
@@ -250,7 +256,7 @@ _internal_write_connection (NMConnection *connection,
 			else
 				filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i);
 
-			filename_escaped = nms_keyfile_utils_escape_filename (filename);
+			filename_escaped = nms_keyfile_utils_escape_filename (filename, with_extension);
 
 			g_free (path);
 			path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped);
@@ -304,7 +310,7 @@ _internal_write_connection (NMConnection *connection,
 		gs_unref_object NMConnection *reread = NULL;
 		gboolean reread_same = FALSE;
 
-		reread = nms_keyfile_reader_from_keyfile (key_file, path, FALSE, NULL);
+		reread = nms_keyfile_reader_from_keyfile (key_file, path, NULL, profile_dir, FALSE, NULL);
 
 		nm_assert (NM_IS_CONNECTION (reread));
 
@@ -337,6 +343,7 @@ _internal_write_connection (NMConnection *connection,
 
 gboolean
 nms_keyfile_writer_connection (NMConnection *connection,
+                               gboolean save_to_disk,
                                const char *existing_path,
                                gboolean force_rename,
                                char **out_path,
@@ -344,9 +351,19 @@ nms_keyfile_writer_connection (NMConnection *connection,
                                gboolean *out_reread_same,
                                GError **error)
 {
+	const char *keyfile_dir;
+
+	if (save_to_disk)
+		keyfile_dir = nms_keyfile_utils_get_path ();
+	else
+		keyfile_dir = NM_CONFIG_KEYFILE_PATH_IN_MEMORY;
+
 	return _internal_write_connection (connection,
+	                                   keyfile_dir,
 	                                   nms_keyfile_utils_get_path (),
-	                                   0, 0,
+	                                   TRUE,
+	                                   0,
+	                                   0,
 	                                   existing_path,
 	                                   force_rename,
 	                                   out_path,
@@ -367,7 +384,10 @@ nms_keyfile_writer_test_connection (NMConnection *connection,
 {
 	return _internal_write_connection (connection,
 	                                   keyfile_dir,
-	                                   owner_uid, owner_grp,
+	                                   keyfile_dir,
+	                                   FALSE,
+	                                   owner_uid,
+	                                   owner_grp,
 	                                   NULL,
 	                                   FALSE,
 	                                   out_path,
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h
index ac41dfa2..030fb7cc 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h
@@ -25,6 +25,7 @@
 #include "nm-connection.h"
 
 gboolean nms_keyfile_writer_connection (NMConnection *connection,
+                                        gboolean save_to_disk,
                                         const char *existing_path,
                                         gboolean force_rename,
                                         char **out_path,
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index b4c6b1e2..4a0e01b3 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -65,37 +65,32 @@ check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int
 	g_assert_cmpint (nm_ip_route_get_metric (route), ==, metric);
 }
 
-static NMConnection *
-keyfile_read_connection_from_file (const char *filename)
-{
-	gs_free_error GError *error = NULL;
-	NMConnection *connection;
-
-	g_assert (filename);
-
-	connection = nms_keyfile_reader_from_file (filename, &error);
-	g_assert_no_error (error);
-
-	nmtst_assert_connection_verifies_without_normalization (connection);
-
-	return connection;
-}
+#define keyfile_read_connection_from_file(full_filename) \
+({ \
+	gs_free_error GError *_error = NULL; \
+	NMConnection *_connection; \
+	\
+	g_assert (full_filename && full_filename[0] == '/'); \
+	\
+	_connection = nms_keyfile_reader_from_file (full_filename, \
+	                                            NULL, \
+	                                            (nmtst_get_rand_int () % 2) ? &_error : NULL); \
+	nmtst_assert_success (_connection, _error); \
+	nmtst_assert_connection_verifies_without_normalization (_connection); \
+	\
+	_connection; \
+})
 
 static void
 assert_reread (NMConnection *connection, gboolean normalize_connection, const char *testfile)
 {
 	gs_unref_object NMConnection *reread = NULL;
 	gs_unref_object NMConnection *connection_clone = NULL;
-	GError *error = NULL;
-	GError **p_error = (nmtst_get_rand_int () % 2) ? &error : NULL;
 	NMSettingConnection *s_con;
 
 	g_assert (NM_IS_CONNECTION (connection));
-	g_assert (testfile && testfile[0]);
 
-	reread = nms_keyfile_reader_from_file (testfile, p_error);
-	g_assert_no_error (error);
-	g_assert (NM_IS_CONNECTION (reread));
+	reread = keyfile_read_connection_from_file (testfile);
 
 	if (   !normalize_connection
 	    && (s_con = nm_connection_get_setting_connection (connection))
@@ -229,10 +224,8 @@ test_read_valid_wired_connection (void)
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
 	NMIPRoute *route;
-	gs_free_error GError *error = NULL;
 	const char *mac;
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
-	gboolean success;
 
 	NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses1*");
 	NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses2*");
@@ -249,16 +242,9 @@ test_read_valid_wired_connection (void)
 	NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address7*");
 	NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*");
 	NMTST_EXPECT_NM_INFO ("*ipv6.route*semicolon at the end*route6*");
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection", &error);
-	g_assert_no_error (error);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection");
 	g_test_assert_expected_messages ();
-	g_assert (connection);
 
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
-
-	/* ===== CONNECTION SETTING ===== */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection");
@@ -266,7 +252,6 @@ test_read_valid_wired_connection (void)
 	g_assert_cmpuint (nm_setting_connection_get_timestamp (s_con), ==, 6654332);
 	g_assert (nm_setting_connection_get_autoconnect (s_con));
 
-	/* ===== WIRED SETTING ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
 
@@ -275,7 +260,6 @@ test_read_valid_wired_connection (void)
 	g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac_address, sizeof (expected_mac_address)));
 	g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1400);
 
-	/* ===== IPv4 SETTING ===== */
 	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);
@@ -324,7 +308,6 @@ test_read_valid_wired_connection (void)
 	nmtst_assert_route_attribute_boolean (route, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, TRUE);
 	nmtst_assert_route_attribute_string  (route, NM_IP_ROUTE_ATTRIBUTE_SRC, "7.7.7.7");
 
-	/* ===== IPv6 SETTING ===== */
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
 
@@ -535,32 +518,22 @@ test_read_ip6_wired_connection (void)
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_IP6", NULL);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_IP6");
 
-	/* ===== CONNECTION SETTING ===== */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection IP6");
 	g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57");
 
-	/* ===== WIRED SETTING ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
 
-	/* ===== IPv4 SETTING ===== */
 	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_DISABLED);
 	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 0);
 
-	/* ===== 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_MANUAL);
@@ -638,28 +611,20 @@ test_read_wired_mac_case (void)
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
-	gs_free_error GError *error = NULL;
 	const char *mac;
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 };
-	gboolean success;
 
 	NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses1*");
 	NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses2*");
 	NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*");
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_MAC_Case", NULL);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_MAC_Case");
 	g_test_assert_expected_messages ();
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
 
-	/* ===== CONNECTION SETTING ===== */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection MAC Case");
 	g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57");
 
-	/* ===== WIRED SETTING ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
 	mac = nm_setting_wired_get_mac_address (s_wired);
@@ -672,29 +637,19 @@ test_read_mac_old_format (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWired *s_wired;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 	const char *mac;
 	char expected_mac[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 };
 	char expected_cloned_mac[ETH_ALEN] = { 0x00, 0x16, 0xaa, 0xbb, 0xcc, 0xfe };
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_MAC_Old_Format", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_MAC_Old_Format");
 
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
 
-	/* MAC address */
 	mac = nm_setting_wired_get_mac_address (s_wired);
 	g_assert (mac);
 	g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, ETH_ALEN));
 
-	/* Cloned MAC address */
 	mac = nm_setting_wired_get_cloned_mac_address (s_wired);
 	g_assert (mac);
 	g_assert (nm_utils_hwaddr_matches (mac, -1, expected_cloned_mac, ETH_ALEN));
@@ -705,20 +660,12 @@ test_read_mac_ib_old_format (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingInfiniband *s_ib;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 	const char *mac;
 	guint8 expected_mac[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
-		0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89,
-		0x90 };
+	    0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89,
+	    0x90 };
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_MAC_IB_Old_Format", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_MAC_IB_Old_Format");
 
 	s_ib = nm_connection_get_setting_infiniband (connection);
 	g_assert (s_ib);
@@ -736,18 +683,11 @@ test_read_valid_wireless_connection (void)
 	NMSettingConnection *s_con;
 	NMSettingWireless *s_wireless;
 	NMSettingIPConfig *s_ip4;
-	gs_free_error GError *error = NULL;
 	const char *bssid;
 	const guint8 expected_bssid[ETH_ALEN] = { 0x00, 0x1a, 0x33, 0x44, 0x99, 0x82 };
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wireless_Connection", NULL);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wireless_Connection");
 
-	/* ===== CONNECTION SETTING ===== */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wireless Connection");
@@ -755,14 +695,12 @@ test_read_valid_wireless_connection (void)
 	g_assert_cmpuint (nm_setting_connection_get_timestamp (s_con), ==, 1226604314);
 	g_assert (nm_setting_connection_get_autoconnect (s_con) == FALSE);
 
-	/* ===== WIRELESS SETTING ===== */
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wireless);
 	bssid = nm_setting_wireless_get_bssid (s_wireless);
 	g_assert (bssid);
 	g_assert (nm_utils_hwaddr_matches (bssid, -1, expected_bssid, sizeof (expected_bssid)));
 
-	/* ===== IPv4 SETTING ===== */
 	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);
@@ -838,24 +776,19 @@ test_read_string_ssid (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWireless *s_wireless;
-	gs_free_error GError *error = NULL;
 	GBytes *ssid;
 	const guint8 *ssid_data;
 	gsize ssid_len;
 	const char *expected_ssid = "blah blah ssid 1234";
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_String_SSID", NULL);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_String_SSID");
 
-	/* ===== WIRELESS SETTING ===== */
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wireless);
+
 	ssid = nm_setting_wireless_get_ssid (s_wireless);
 	g_assert (ssid);
+
 	ssid_data = g_bytes_get_data (ssid, &ssid_len);
 	g_assert_cmpmem (ssid_data, ssid_len, expected_ssid, strlen (expected_ssid));
 }
@@ -922,22 +855,13 @@ test_read_intlist_ssid (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWireless *s_wifi;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 	GBytes *ssid;
 	const guint8 *ssid_data;
 	gsize ssid_len;
 	const char *expected_ssid = "blah1234";
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Intlist_SSID", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlist_SSID");
 
-	/* SSID */
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
 
@@ -1015,16 +939,10 @@ test_read_intlike_ssid (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWireless *s_wifi;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 	GBytes *ssid;
 	const char *expected_ssid = "101";
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID", &error);
-	nmtst_assert_success (connection, error);
-
-	success = nm_connection_verify (connection, &error);
-	nmtst_assert_success (success, error);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID");
 
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
@@ -1039,16 +957,10 @@ test_read_intlike_ssid_2 (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWireless *s_wifi;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 	GBytes *ssid;
 	const char *expected_ssid = "11;12;13;";
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID_2", &error);
-	nmtst_assert_success (connection, error);
-
-	success = nm_connection_verify (connection, &error);
-	nmtst_assert_success (success, error);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID_2");
 
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
@@ -1183,24 +1095,16 @@ test_read_bt_dun_connection (void)
 	NMSettingBluetooth *s_bluetooth;
 	NMSettingSerial *s_serial;
 	NMSettingGsm *s_gsm;
-	gs_free_error GError *error = NULL;
 	const char *bdaddr;
 	const guint8 expected_bdaddr[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_BT", NULL);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_BT");
 
-	/* ===== CONNECTION SETTING ===== */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "AT&T Data Connect BT");
 	g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "089130ab-ce28-46e4-ad77-d44869b03d19");
 
-	/* ===== BLUETOOTH SETTING ===== */
 	s_bluetooth = nm_connection_get_setting_bluetooth (connection);
 	g_assert (s_bluetooth);
 	bdaddr = nm_setting_bluetooth_get_bdaddr (s_bluetooth);
@@ -1208,14 +1112,12 @@ test_read_bt_dun_connection (void)
 	g_assert (nm_utils_hwaddr_matches (bdaddr, -1, expected_bdaddr, sizeof (expected_bdaddr)));
 	g_assert_cmpstr (nm_setting_bluetooth_get_connection_type (s_bluetooth), ==, NM_SETTING_BLUETOOTH_TYPE_DUN);
 
-	/* ===== GSM SETTING ===== */
 	s_gsm = nm_connection_get_setting_gsm (connection);
 	g_assert (s_gsm);
 	g_assert_cmpstr (nm_setting_gsm_get_apn (s_gsm), ==, "ISP.CINGULAR");
 	g_assert_cmpstr (nm_setting_gsm_get_username (s_gsm), ==, "ISP@CINGULARGPRS.COM");
 	g_assert_cmpstr (nm_setting_gsm_get_password (s_gsm), ==, "CINGULAR1");
 
-	/* ===== SERIAL SETTING ===== */
 	s_serial = nm_connection_get_setting_serial (connection);
 	g_assert (s_serial);
 	g_assert (nm_setting_serial_get_parity (s_serial) == NM_SETTING_SERIAL_PARITY_ODD);
@@ -1288,28 +1190,17 @@ test_read_gsm_connection (void)
 	NMSettingConnection *s_con;
 	NMSettingSerial *s_serial;
 	NMSettingGsm *s_gsm;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_Plain", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_Plain");
 
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
-
-	/* ===== CONNECTION SETTING ===== */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "AT&T Data Connect");
 	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_GSM_SETTING_NAME);
 
-	/* ===== BLUETOOTH SETTING ===== */
 	/* Plain GSM, so no BT setting expected */
 	g_assert (nm_connection_get_setting_bluetooth (connection) == NULL);
 
-	/* ===== GSM SETTING ===== */
 	s_gsm = nm_connection_get_setting_gsm (connection);
 	g_assert (s_gsm);
 	g_assert_cmpstr (nm_setting_gsm_get_apn (s_gsm), ==, "ISP.CINGULAR");
@@ -1321,7 +1212,6 @@ test_read_gsm_connection (void)
 	g_assert_cmpstr (nm_setting_gsm_get_sim_id (s_gsm), ==, "89148000000060671234");
 	g_assert_cmpstr (nm_setting_gsm_get_sim_operator_id (s_gsm), ==, "310260");
 
-	/* ===== SERIAL SETTING ===== */
 	s_serial = nm_connection_get_setting_serial (connection);
 	g_assert (s_serial);
 	g_assert_cmpint (nm_setting_serial_get_parity (s_serial), ==, NM_SETTING_SERIAL_PARITY_ODD);
@@ -1387,25 +1277,16 @@ test_read_wired_8021x_tls_blob_connection (void)
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWired *s_wired;
 	NMSetting8021x *s_8021x;
-	gs_free_error GError *error = NULL;
 	const char *tmp;
-	gboolean success;
 	GBytes *blob;
 
 	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Blob", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Blob");
 
-	/* ===== Wired Setting ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired != NULL);
 
-	/* ===== 802.1x Setting ===== */
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 	g_assert (s_8021x != NULL);
 
@@ -1445,25 +1326,16 @@ test_read_wired_8021x_tls_bad_path_connection (void)
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWired *s_wired;
 	NMSetting8021x *s_8021x;
-	gs_free_error GError *error = NULL;
 	const char *tmp;
 	char *tmp2;
-	gboolean success;
 
 	NMTST_EXPECT_NM_WARN ("*does not exist*");
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Path_Missing", &error);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Path_Missing");
 	g_test_assert_expected_messages ();
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
 
-	/* ===== Wired Setting ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired != NULL);
 
-	/* ===== 802.1x Setting ===== */
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 	g_assert (s_8021x != NULL);
 
@@ -1499,25 +1371,16 @@ test_read_wired_8021x_tls_old_connection (void)
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWired *s_wired;
 	NMSetting8021x *s_8021x;
-	gs_free_error GError *error = NULL;
 	const char *tmp;
-	gboolean success;
 
 	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*");
 	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Old", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Old");
 
-	/* ===== Wired Setting ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired != NULL);
 
-	/* ===== 802.1x Setting ===== */
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 	g_assert (s_8021x != NULL);
 
@@ -1547,23 +1410,14 @@ test_read_wired_8021x_tls_new_connection (void)
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingWired *s_wired;
 	NMSetting8021x *s_8021x;
-	gs_free_error GError *error = NULL;
 	const char *tmp;
 	char *tmp2;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_New", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_New");
 
-	/* ===== Wired Setting ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired != NULL);
 
-	/* ===== 802.1x Setting ===== */
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 	g_assert (s_8021x != NULL);
 
@@ -1684,7 +1538,6 @@ test_write_wired_8021x_tls_connection_path (void)
 	char *tmp, *tmp2;
 	gboolean success;
 	gs_free char *testfile = NULL;
-	gs_free_error GError *error = NULL;
 	gs_unref_keyfile GKeyFile *keyfile = NULL;
 	gboolean relative = FALSE;
 	gboolean reread_same = FALSE;
@@ -1701,12 +1554,7 @@ test_write_wired_8021x_tls_connection_path (void)
 	g_clear_object (&reread);
 
 	/* Read the connection back in and compare it to the one we just wrote out */
-	reread = nms_keyfile_reader_from_file (testfile, &error);
-	if (!reread) {
-		g_assert (error);
-		g_warning ("Failed to re-read test connection: %s", error->message);
-		g_assert (reread);
-	}
+	reread = keyfile_read_connection_from_file (testfile);
 
 	success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT);
 	if (!reread) {
@@ -1772,7 +1620,6 @@ test_write_wired_8021x_tls_connection_blob (void)
 	char *new_priv_key;
 	const char *uuid;
 	gboolean reread_same = FALSE;
-	gs_free_error GError *error = NULL;
 	GBytes *password_raw;
 
 #define PASSWORD_RAW "password-raw\0test"
@@ -1817,12 +1664,7 @@ test_write_wired_8021x_tls_connection_blob (void)
 	g_assert (g_file_test (new_priv_key, G_FILE_TEST_EXISTS));
 
 	/* Read the connection back in and compare it to the one we just wrote out */
-	reread = nms_keyfile_reader_from_file (testfile, &error);
-	if (!reread) {
-		g_assert (error);
-		g_warning ("Failed to re-read test connection: %s", error->message);
-		g_assert (reread);
-	}
+	reread = keyfile_read_connection_from_file (testfile);
 
 	/* Ensure the re-read connection's certificates use the path scheme */
 	s_8021x = nm_connection_get_setting_802_1x (reread);
@@ -1862,29 +1704,20 @@ test_read_infiniband_connection (void)
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	NMSettingInfiniband *s_ib;
-	gs_free_error GError *error = NULL;
 	const char *mac;
 	guint8 expected_mac[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
-		0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89,
-		0x90 };
+	    0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89,
+	    0x90 };
 	const char *expected_id = "Test InfiniBand Connection";
 	const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57";
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_InfiniBand_Connection", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_InfiniBand_Connection");
 
-	/* Connection setting */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id);
 	g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid);
 
-	/* InfiniBand setting */
 	s_ib = nm_connection_get_setting_infiniband (connection);
 	g_assert (s_ib);
 
@@ -1952,31 +1785,21 @@ test_read_bridge_main (void)
 	NMSettingConnection *s_con;
 	NMSettingIPConfig *s_ip4;
 	NMSettingBridge *s_bridge;
-	gs_free_error GError *error = NULL;
 	const char *expected_id = "Test Bridge Main";
 	const char *expected_uuid = "8f061643-fe41-4d4c-a8d9-097d26e2ad3a";
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Main", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Main");
 
-	/* Connection setting */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id);
 	g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid);
 	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "br0");
 
-	/* IPv4 setting */
 	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);
 
-	/* Bridge setting */
 	s_bridge = nm_connection_get_setting_bridge (connection);
 	g_assert (s_bridge);
 	g_assert_cmpuint (nm_setting_bridge_get_forward_delay (s_bridge), ==, 2);
@@ -2049,19 +1872,11 @@ test_read_bridge_component (void)
 	NMSettingWired *s_wired;
 	const char *mac;
 	guint8 expected_mac[ETH_ALEN] = { 0x00, 0x22, 0x15, 0x59, 0x62, 0x97 };
-	gs_free_error GError *error = NULL;
 	const char *expected_id = "Test Bridge Component";
 	const char *expected_uuid = "d7b4f96c-c45e-4298-bef8-f48574f8c1c0";
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Component", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Component");
 
-	/* Connection setting */
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id);
@@ -2069,14 +1884,12 @@ test_read_bridge_component (void)
 	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, "br0");
 	g_assert (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BRIDGE_SETTING_NAME));
 
-	/* Wired setting */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
 	mac = nm_setting_wired_get_mac_address (s_wired);
 	g_assert (mac);
 	g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac)));
 
-	/* BridgePort setting */
 	s_port = nm_connection_get_setting_bridge_port (connection);
 	g_assert (s_port);
 	g_assert (nm_setting_bridge_port_get_hairpin_mode (s_port));
@@ -2141,17 +1954,9 @@ test_read_new_wired_group_name (void)
 	NMSettingWired *s_wired;
 	const char *mac;
 	guint8 expected_mac[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_New_Wired_Group_Name", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wired_Group_Name");
 
-	/* Wired setting */
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
 	g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1400);
@@ -2221,14 +2026,8 @@ test_read_new_wireless_group_names (void)
 	NMSettingWirelessSecurity *s_wsec;
 	GBytes *ssid;
 	const char *expected_ssid = "foobar";
-	gs_free_error GError *error = NULL;
-	gboolean success;
-
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_New_Wireless_Group_Names", &error);
-	nmtst_assert_success (connection, error);
 
-	success = nm_connection_verify (connection, &error);
-	nmtst_assert_success (success, error);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wireless_Group_Names");
 
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
@@ -2323,17 +2122,9 @@ test_read_missing_vlan_setting (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingVlan *s_vlan;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Setting", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Setting");
 
-	/* Ensure the VLAN setting exists */
 	s_vlan = nm_connection_get_setting_vlan (connection);
 	g_assert (s_vlan);
 	g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 0);
@@ -2345,17 +2136,9 @@ test_read_missing_vlan_flags (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingVlan *s_vlan;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Flags", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Flags");
 
-	/* Ensure the VLAN setting exists */
 	s_vlan = nm_connection_get_setting_vlan (connection);
 	g_assert (s_vlan);
 
@@ -2368,19 +2151,15 @@ static void
 test_read_missing_id_uuid (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
-	gs_free_error GError *error = NULL;
-	gboolean success;
+	gs_free char *expected_uuid = NULL;
+	const char *FILENAME = TEST_KEYFILES_DIR"/Test_Missing_ID_UUID";
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_Missing_ID_UUID", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	expected_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", FILENAME, NULL);
+
+	connection = keyfile_read_connection_from_file (FILENAME);
 
-	/* Ensure the ID and UUID properties are there */
 	g_assert_cmpstr (nm_connection_get_id (connection), ==, "Test_Missing_ID_UUID");
-	g_assert (nm_connection_get_uuid (connection));
+	g_assert_cmpstr (nm_connection_get_uuid (connection), ==, expected_uuid);
 }
 
 static void
@@ -2468,17 +2247,9 @@ test_read_enum_property (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingIPConfig *s_ip6;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_Enum_Property", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Enum_Property");
 
-	/* IPv6 setting */
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
 	g_assert_cmpint (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)), ==, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR);
@@ -2528,17 +2299,9 @@ test_read_flags_property (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingGsm *s_gsm;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_Flags_Property", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Flags_Property");
 
-	/* GSM setting */
 	s_gsm = nm_connection_get_setting_gsm (connection);
 	g_assert (s_gsm);
 	g_assert_cmpint (nm_setting_gsm_get_password_flags (s_gsm), ==,
@@ -2591,14 +2354,8 @@ test_read_tc_config (void)
 	NMTCQdisc *qdisc1, *qdisc2;
 	NMTCAction *action1, *action2;
 	NMTCTfilter *tfilter1, *tfilter2;
-	gs_free_error GError *error = NULL;
-	gboolean success;
 
-	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_TC_Config", NULL);
-	g_assert (connection);
-	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_TC_Config");
 
 	s_tc = nm_connection_get_setting_tc_config (connection);
 	g_assert (s_tc);
@@ -2703,49 +2460,51 @@ test_write_tc_config (void)
 /*****************************************************************************/
 
 static void
-_escape_filename (const char *filename, gboolean would_be_ignored)
+_escape_filename (gboolean with_extension, const char *filename, gboolean would_be_ignored)
 {
 	gs_free char *esc = NULL;
 
 	g_assert (filename && filename[0]);
 
-	if (!!would_be_ignored != !!nms_keyfile_utils_should_ignore_file (filename)) {
+	if (!!would_be_ignored != !!nms_keyfile_utils_should_ignore_file (filename, with_extension)) {
 		if (would_be_ignored)
 			g_error ("We expect filename \"%s\" to be ignored, but it isn't", filename);
 		else
 			g_error ("We expect filename \"%s\" not to be ignored, but it is", filename);
 	}
 
-	esc = nms_keyfile_utils_escape_filename (filename);
+	esc = nms_keyfile_utils_escape_filename (filename, with_extension);
 	g_assert (esc && esc[0]);
 	g_assert (!strchr (esc, '/'));
 
-	if (nms_keyfile_utils_should_ignore_file (esc))
+	if (nms_keyfile_utils_should_ignore_file (esc, with_extension))
 		g_error ("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc);
 }
 
 static void
 test_nm_keyfile_plugin_utils_escape_filename (void)
 {
-	_escape_filename ("ab", FALSE);
-	_escape_filename (".vim-file.swp", TRUE);
-	_escape_filename (".vim-file.Swp", TRUE);
-	_escape_filename (".vim-file.SWP", TRUE);
-	_escape_filename (".vim-file.swpx", TRUE);
-	_escape_filename (".vim-file.Swpx", TRUE);
-	_escape_filename (".vim-file.SWPX", TRUE);
-	_escape_filename (".pem-file.pem", TRUE);
-	_escape_filename (".pem-file.Pem", TRUE);
-	_escape_filename (".pem-file.PEM", TRUE);
-	_escape_filename (".pem-file.der", TRUE);
-	_escape_filename (".pem-file.Der", TRUE);
-	_escape_filename (".mkstemp.ABCEDF", TRUE);
-	_escape_filename (".mkstemp.abcdef", TRUE);
-	_escape_filename (".mkstemp.123456", TRUE);
-	_escape_filename (".mkstemp.A23456", TRUE);
-	_escape_filename (".#emacs-locking", TRUE);
-	_escape_filename ("file-with-tilde~", TRUE);
-	_escape_filename (".file-with-dot", TRUE);
+	_escape_filename (FALSE, "ab", FALSE);
+	_escape_filename (FALSE, ".vim-file.swp", TRUE);
+	_escape_filename (FALSE, ".vim-file.Swp", TRUE);
+	_escape_filename (FALSE, ".vim-file.SWP", TRUE);
+	_escape_filename (FALSE, ".vim-file.swpx", TRUE);
+	_escape_filename (FALSE, ".vim-file.Swpx", TRUE);
+	_escape_filename (FALSE, ".vim-file.SWPX", TRUE);
+	_escape_filename (FALSE, ".pem-file.pem", TRUE);
+	_escape_filename (FALSE, ".pem-file.Pem", TRUE);
+	_escape_filename (FALSE, ".pem-file.PEM", TRUE);
+	_escape_filename (FALSE, ".pem-file.der", TRUE);
+	_escape_filename (FALSE, ".pem-file.Der", TRUE);
+	_escape_filename (FALSE, ".mkstemp.ABCEDF", TRUE);
+	_escape_filename (FALSE, ".mkstemp.abcdef", TRUE);
+	_escape_filename (FALSE, ".mkstemp.123456", TRUE);
+	_escape_filename (FALSE, ".mkstemp.A23456", TRUE);
+	_escape_filename (FALSE, ".#emacs-locking", TRUE);
+	_escape_filename (FALSE, "file-with-tilde~", TRUE);
+	_escape_filename (FALSE, ".file-with-dot", TRUE);
+
+	_escape_filename (TRUE, "lala", TRUE);
 }
 
 /*****************************************************************************/
diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c
index 41d510e8..043b5550 100644
--- a/src/supplicant/nm-supplicant-config.c
+++ b/src/supplicant/nm-supplicant-config.c
@@ -483,6 +483,12 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
 	if (is_ap) {
 		if (!nm_supplicant_config_add_option (self, "mode", "2", -1, NULL, error))
 			return FALSE;
+
+		if (   nm_setting_wireless_get_hidden (setting)
+		    && !nm_supplicant_config_add_option (self,
+		                                         "ignore_broadcast_ssid", "1",
+		                                         -1, NULL, error))
+			return FALSE;
 	}
 
 	if ((is_adhoc || is_ap) && fixed_freq) {
diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c
index 8a0cadc7..1e25675d 100644
--- a/src/supplicant/nm-supplicant-settings-verify.c
+++ b/src/supplicant/nm-supplicant-settings-verify.c
@@ -151,6 +151,7 @@ static const struct Opt opt_table[] = {
 	{ "mka_ckn",            TYPE_BYTES,   0, 65536, FALSE, NULL },
 	{ "macsec_port",        TYPE_INT,     1, 65534, FALSE, NULL },
 	{ "ieee80211w",         TYPE_INT,     0, 2, FALSE, NULL },
+	{ "ignore_broadcast_ssid", TYPE_INT,  0, 2, FALSE, NULL },
 };
 
 static gboolean
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index f1cbd6a4..06e2e532 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -91,7 +91,7 @@ int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd);
 int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
-int dhcp6_option_parse_status(DHCP6Option *option);
+int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
 int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
                                 struct in6_addr **addrs, size_t count,
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index a8a56463..ff1cbf13 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -249,10 +249,11 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
         return 0;
 }
 
-int dhcp6_option_parse_status(DHCP6Option *option) {
+int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
         DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
 
-        if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt))
+        if (len < sizeof(DHCP6StatusOption) ||
+            be16toh(option->len) + sizeof(DHCP6Option) < sizeof(DHCP6StatusOption))
                 return -ENOBUFS;
 
         return be16toh(statusopt->status);
@@ -279,7 +280,7 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
         }
 
         if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) {
-                r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options);
+                r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*addr_option));
                 if (r != 0)
                         return r < 0 ? r: 0;
         }
@@ -319,7 +320,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
         }
 
         if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) {
-                r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options);
+                r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*pdprefix_option));
                 if (r != 0)
                         return r < 0 ? r: 0;
         }
@@ -464,7 +465,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
 
                 case SD_DHCP6_OPTION_STATUS_CODE:
 
-                        status = dhcp6_option_parse_status(option);
+                        status = dhcp6_option_parse_status(option, optlen);
                         if (status) {
                                 log_dhcp6_client(client, "IA status %d",
                                                  status);
@@ -565,7 +566,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
                                 /* Literal label */
                                 label = (const char *)&optval[pos];
                                 pos += c;
-                                if (pos > optlen)
+                                if (pos >= optlen)
                                         return -EMSGSIZE;
 
                                 if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index 882a7eb8..8444a750 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -870,7 +870,7 @@ static int client_parse_message(
                         break;
 
                 case SD_DHCP6_OPTION_STATUS_CODE:
-                        status = dhcp6_option_parse_status(option);
+                        status = dhcp6_option_parse_status(option, optlen);
                         if (status) {
                                 log_dhcp6_client(client, "%s Status %s",
                                                  dhcp6_message_type_to_string(message->type),