about summary refs log tree commit diff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/NetworkManagerUtils.c270
-rw-r--r--src/core/NetworkManagerUtils.h36
-rw-r--r--src/core/devices/adsl/meson.build1
-rw-r--r--src/core/devices/adsl/nm-atm-manager.c4
-rw-r--r--src/core/devices/adsl/nm-device-adsl.c10
-rw-r--r--src/core/devices/bluetooth/meson.build2
-rw-r--r--src/core/devices/bluetooth/nm-bluez-manager.c20
-rw-r--r--src/core/devices/bluetooth/nm-device-bt.c18
-rw-r--r--src/core/devices/nm-acd-manager.c2
-rw-r--r--src/core/devices/nm-device-6lowpan.c2
-rw-r--r--src/core/devices/nm-device-bond.c54
-rw-r--r--src/core/devices/nm-device-bridge.c18
-rw-r--r--src/core/devices/nm-device-dummy.c12
-rw-r--r--src/core/devices/nm-device-ethernet.c58
-rw-r--r--src/core/devices/nm-device-factory.c4
-rw-r--r--src/core/devices/nm-device-generic.c12
-rw-r--r--src/core/devices/nm-device-infiniband.c14
-rw-r--r--src/core/devices/nm-device-ip-tunnel.c62
-rw-r--r--src/core/devices/nm-device-macsec.c66
-rw-r--r--src/core/devices/nm-device-macvlan.c18
-rw-r--r--src/core/devices/nm-device-ppp.c7
-rw-r--r--src/core/devices/nm-device-private.h10
-rw-r--r--src/core/devices/nm-device-tun.c30
-rw-r--r--src/core/devices/nm-device-utils.c356
-rw-r--r--src/core/devices/nm-device-utils.h98
-rw-r--r--src/core/devices/nm-device-veth.c10
-rw-r--r--src/core/devices/nm-device-vlan.c33
-rw-r--r--src/core/devices/nm-device-vrf.c4
-rw-r--r--src/core/devices/nm-device-vxlan.c62
-rw-r--r--src/core/devices/nm-device-wireguard.c22
-rw-r--r--src/core/devices/nm-device-wpan.c4
-rw-r--r--src/core/devices/nm-device.c1295
-rw-r--r--src/core/devices/nm-device.h97
-rw-r--r--src/core/devices/nm-lldp-listener.c402
-rw-r--r--src/core/devices/nm-lldp-listener.h31
-rw-r--r--src/core/devices/ovs/meson.build1
-rw-r--r--src/core/devices/ovs/nm-device-ovs-bridge.c6
-rw-r--r--src/core/devices/ovs/nm-device-ovs-interface.c15
-rw-r--r--src/core/devices/ovs/nm-device-ovs-port.c4
-rw-r--r--src/core/devices/ovs/nm-ovs-factory.c4
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c6
-rw-r--r--src/core/devices/team/meson.build1
-rw-r--r--src/core/devices/team/nm-device-team.c32
-rw-r--r--src/core/devices/team/nm-team-factory.c4
-rw-r--r--src/core/devices/tests/test-acd.c4
-rw-r--r--src/core/devices/tests/test-lldp.c66
-rw-r--r--src/core/devices/wifi/meson.build2
-rw-r--r--src/core/devices/wifi/nm-device-iwd.c206
-rw-r--r--src/core/devices/wifi/nm-device-olpc-mesh.c32
-rw-r--r--src/core/devices/wifi/nm-device-wifi-p2p.c9
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c145
-rw-r--r--src/core/devices/wifi/nm-device-wifi.h2
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.c435
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.h1
-rw-r--r--src/core/devices/wifi/nm-wifi-ap.c153
-rw-r--r--src/core/devices/wifi/nm-wifi-ap.h3
-rw-r--r--src/core/devices/wifi/nm-wifi-common.c38
-rw-r--r--src/core/devices/wifi/nm-wifi-factory.c15
-rw-r--r--src/core/devices/wifi/nm-wifi-p2p-peer.c7
-rw-r--r--src/core/devices/wifi/nm-wifi-utils.c902
-rw-r--r--src/core/devices/wifi/nm-wifi-utils.h9
-rw-r--r--src/core/devices/wifi/tests/test-devices-wifi.c58
-rw-r--r--src/core/devices/wwan/meson.build2
-rw-r--r--src/core/devices/wwan/nm-device-modem.c17
-rw-r--r--src/core/devices/wwan/nm-modem-broadband.c14
-rw-r--r--src/core/devices/wwan/nm-modem-manager.c2
-rw-r--r--src/core/devices/wwan/nm-modem-ofono.c172
-rw-r--r--src/core/devices/wwan/nm-modem-ofono.h2
-rw-r--r--src/core/devices/wwan/nm-modem.c13
-rw-r--r--src/core/devices/wwan/nm-wwan-factory.c2
-rw-r--r--src/core/dhcp/meson.build7
-rw-r--r--src/core/dhcp/nm-dhcp-client.c167
-rw-r--r--src/core/dhcp/nm-dhcp-client.h31
-rw-r--r--src/core/dhcp/nm-dhcp-dhclient-utils.c13
-rw-r--r--src/core/dhcp/nm-dhcp-dhclient.c56
-rw-r--r--src/core/dhcp/nm-dhcp-dhcpcanon.c5
-rw-r--r--src/core/dhcp/nm-dhcp-dhcpcd.c49
-rw-r--r--src/core/dhcp/nm-dhcp-helper.c10
-rw-r--r--src/core/dhcp/nm-dhcp-listener.c2
-rw-r--r--src/core/dhcp/nm-dhcp-manager.c107
-rw-r--r--src/core/dhcp/nm-dhcp-manager.h7
-rw-r--r--src/core/dhcp/nm-dhcp-nettools.c60
-rw-r--r--src/core/dhcp/nm-dhcp-options.c2
-rw-r--r--src/core/dhcp/nm-dhcp-systemd.c130
-rw-r--r--src/core/dhcp/nm-dhcp-utils.c16
-rw-r--r--src/core/dhcp/tests/test-dhcp-dhclient.c4
-rw-r--r--src/core/dhcp/tests/test-dhcp-utils.c4
-rw-r--r--src/core/dns/nm-dns-dnsmasq.c6
-rw-r--r--src/core/dns/nm-dns-dnsmasq.h1
-rw-r--r--src/core/dns/nm-dns-manager.c168
-rw-r--r--src/core/dns/nm-dns-manager.h3
-rw-r--r--src/core/dns/nm-dns-plugin.c2
-rw-r--r--src/core/dns/nm-dns-plugin.h3
-rw-r--r--src/core/dns/nm-dns-systemd-resolved.c465
-rw-r--r--src/core/dns/nm-dns-systemd-resolved.h31
-rw-r--r--src/core/dnsmasq/nm-dnsmasq-manager.c2
-rw-r--r--src/core/dnsmasq/nm-dnsmasq-utils.c5
-rw-r--r--src/core/dnsmasq/nm-dnsmasq-utils.h2
-rw-r--r--src/core/initrd/meson.build31
-rw-r--r--src/core/initrd/nm-initrd-generator.c226
-rw-r--r--src/core/initrd/nm-initrd-generator.h45
-rw-r--r--src/core/initrd/nmi-cmdline-reader.c1269
-rw-r--r--src/core/initrd/nmi-dt-reader.c371
-rw-r--r--src/core/initrd/nmi-ibft-reader.c463
-rw-r--r--src/core/initrd/tests/meson.build24
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/class/net/eth1/address1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port1
-rw-r--r--src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpathbin108 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-requestbin273 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-responsebin300 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpathbin37 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip1
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip2
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-namebin9 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-namebin15 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip2
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/namebin7 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ipbin4 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-pathbin1 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip2
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-filebin11 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-optionsbin1 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_typebin8 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address1
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address1
-rw-r--r--src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/namebin9 -> 0 bytes
-rw-r--r--src/core/initrd/tests/sysfs-static/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs-static/class/net/eth1/address1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target0/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target1/index1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target1/lun1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target1/port1
-rw-r--r--src/core/initrd/tests/sysfs-static/firmware/ibft/target1/target-name1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name1
-rw-r--r--src/core/initrd/tests/sysfs/class/net/eth0/address1
-rw-r--r--src/core/initrd/tests/sysfs/class/net/eth2/address1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/signature1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/flags1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/index1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/mac1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/origin1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/flags1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/index1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/mac1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/origin1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/initiator/flags1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/initiator/index1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/initiator/isns-server1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/initiator/slp-server1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target0/chap-type1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target0/flags1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target0/index1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target0/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target0/lun1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target0/port1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/chap-type1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/flags1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/index1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/ip-addr1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/lun1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/port1
-rw-r--r--src/core/initrd/tests/sysfs/firmware/ibft/target2/target-name1
-rw-r--r--src/core/initrd/tests/test-cmdline-reader.c2182
-rw-r--r--src/core/initrd/tests/test-dt-reader.c144
-rw-r--r--src/core/initrd/tests/test-ibft-reader.c303
-rw-r--r--src/core/main.c9
-rw-r--r--src/core/meson.build138
-rw-r--r--src/core/ndisc/nm-lndp-ndisc.c24
-rw-r--r--src/core/ndisc/nm-ndisc.c8
-rw-r--r--src/core/ndisc/nm-ndisc.h14
-rw-r--r--src/core/ndisc/tests/test-ndisc-linux.c2
-rw-r--r--src/core/nm-act-request.c34
-rw-r--r--src/core/nm-act-request.h6
-rw-r--r--src/core/nm-active-connection.c99
-rw-r--r--src/core/nm-audit-manager.c155
-rw-r--r--src/core/nm-auth-manager.c4
-rw-r--r--src/core/nm-auth-manager.h2
-rw-r--r--src/core/nm-auth-utils.c4
-rw-r--r--src/core/nm-checkpoint.c34
-rw-r--r--src/core/nm-config-data.c220
-rw-r--r--src/core/nm-config-data.h118
-rw-r--r--src/core/nm-config.c20
-rw-r--r--src/core/nm-config.h80
-rw-r--r--src/core/nm-connectivity.c8
-rw-r--r--src/core/nm-connectivity.h1
-rw-r--r--src/core/nm-core-utils.c845
-rw-r--r--src/core/nm-core-utils.h185
-rw-r--r--src/core/nm-dbus-manager.c91
-rw-r--r--src/core/nm-dbus-object.c10
-rw-r--r--src/core/nm-dbus-object.h15
-rw-r--r--src/core/nm-dbus-utils.c4
-rw-r--r--src/core/nm-dbus-utils.h118
-rw-r--r--src/core/nm-dcb.c2
-rw-r--r--src/core/nm-default-daemon.h4
-rw-r--r--src/core/nm-dhcp-config.c34
-rw-r--r--src/core/nm-dispatcher.c24
-rw-r--r--src/core/nm-firewall-manager.h54
-rw-r--r--src/core/nm-firewall-utils.c816
-rw-r--r--src/core/nm-firewall-utils.h29
-rw-r--r--src/core/nm-firewalld-manager.c (renamed from src/core/nm-firewall-manager.c)194
-rw-r--r--src/core/nm-firewalld-manager.h54
-rw-r--r--src/core/nm-hostname-manager.c6
-rw-r--r--src/core/nm-iface-helper.c11
-rw-r--r--src/core/nm-ip4-config.c119
-rw-r--r--src/core/nm-ip4-config.h7
-rw-r--r--src/core/nm-ip6-config.c125
-rw-r--r--src/core/nm-ip6-config.h6
-rw-r--r--src/core/nm-keep-alive.c2
-rw-r--r--src/core/nm-l3-config-data.c342
-rw-r--r--src/core/nm-l3-config-data.h73
-rw-r--r--src/core/nm-l3cfg.c75
-rw-r--r--src/core/nm-l3cfg.h25
-rw-r--r--src/core/nm-manager.c416
-rw-r--r--src/core/nm-netns.c16
-rw-r--r--src/core/nm-netns.h10
-rw-r--r--src/core/nm-pacrunner-manager.c5
-rw-r--r--src/core/nm-policy.c38
-rw-r--r--src/core/nm-proxy-config.c2
-rw-r--r--src/core/nm-proxy-config.h3
-rw-r--r--src/core/nm-rfkill-manager.c2
-rw-r--r--src/core/nm-sleep-monitor.c4
-rw-r--r--src/core/nm-test-utils-core.h38
-rw-r--r--src/core/nm-types.h163
-rw-r--r--src/core/platform/linux/nl802154.h450
-rw-r--r--src/core/platform/nm-fake-platform.c116
-rw-r--r--src/core/platform/nm-fake-platform.h2
-rw-r--r--src/core/platform/nm-linux-platform.c9696
-rw-r--r--src/core/platform/nm-linux-platform.h30
-rw-r--r--src/core/platform/nm-platform-private.h29
-rw-r--r--src/core/platform/nm-platform.c8966
-rw-r--r--src/core/platform/nm-platform.h2372
-rw-r--r--src/core/platform/nmp-object.c3457
-rw-r--r--src/core/platform/nmp-object.h1144
-rw-r--r--src/core/platform/nmp-rules-manager.c808
-rw-r--r--src/core/platform/nmp-rules-manager.h53
-rw-r--r--src/core/platform/tests/monitor.c2
-rw-r--r--src/core/platform/tests/test-address.c6
-rw-r--r--src/core/platform/tests/test-cleanup.c9
-rw-r--r--src/core/platform/tests/test-common.c47
-rw-r--r--src/core/platform/tests/test-common.h31
-rw-r--r--src/core/platform/tests/test-link.c114
-rw-r--r--src/core/platform/tests/test-nmp-object.c4
-rw-r--r--src/core/platform/tests/test-platform-general.c10
-rw-r--r--src/core/platform/tests/test-route.c4
-rw-r--r--src/core/platform/tests/test-tc.c6
-rw-r--r--src/core/platform/wifi/nm-wifi-utils-nl80211.c909
-rw-r--r--src/core/platform/wifi/nm-wifi-utils-nl80211.h29
-rw-r--r--src/core/platform/wifi/nm-wifi-utils-private.h66
-rw-r--r--src/core/platform/wifi/nm-wifi-utils-wext.c836
-rw-r--r--src/core/platform/wifi/nm-wifi-utils-wext.h28
-rw-r--r--src/core/platform/wifi/nm-wifi-utils.c211
-rw-r--r--src/core/platform/wifi/nm-wifi-utils.h74
-rw-r--r--src/core/platform/wpan/nm-wpan-utils.c286
-rw-r--r--src/core/platform/wpan/nm-wpan-utils.h37
-rw-r--r--src/core/ppp/meson.build17
-rw-r--r--src/core/ppp/nm-ppp-manager-call.c47
-rw-r--r--src/core/ppp/nm-ppp-manager.c13
-rw-r--r--src/core/ppp/nm-pppd-plugin.c20
-rw-r--r--src/core/settings/nm-agent-manager.c4
-rw-r--r--src/core/settings/nm-secret-agent.c10
-rw-r--r--src/core/settings/nm-settings-connection.c325
-rw-r--r--src/core/settings/nm-settings-connection.h10
-rw-r--r--src/core/settings/nm-settings-plugin.c2
-rw-r--r--src/core/settings/nm-settings-storage.c5
-rw-r--r--src/core/settings/nm-settings-storage.h15
-rw-r--r--src/core/settings/nm-settings.c187
-rw-r--r--src/core/settings/nm-settings.h5
-rw-r--r--src/core/settings/plugins/ifcfg-rh/meson.build2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c10
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c65
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c5
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c19
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c218
-rw-r--r--src/core/settings/plugins/ifcfg-rh/shvar.c127
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected14
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c354
-rw-r--r--src/core/settings/plugins/ifupdown/meson.build2
-rw-r--r--src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c26
-rw-r--r--src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c2
-rw-r--r--src/core/settings/plugins/ifupdown/tests/test-ifupdown.c2
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-plugin.c28
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-reader.c2
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-storage.c7
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-utils.c38
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-writer.c9
-rw-r--r--src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c5
-rw-r--r--src/core/supplicant/nm-supplicant-config.c174
-rw-r--r--src/core/supplicant/nm-supplicant-interface.c200
-rw-r--r--src/core/supplicant/nm-supplicant-manager.c41
-rw-r--r--src/core/supplicant/nm-supplicant-settings-verify.c1
-rw-r--r--src/core/supplicant/nm-supplicant-types.h26
-rw-r--r--src/core/supplicant/tests/test-supplicant-config.c39
-rw-r--r--src/core/systemd/meson.build62
-rw-r--r--src/core/systemd/nm-default-systemd.h18
-rw-r--r--src/core/systemd/nm-sd-utils-core.c27
-rw-r--r--src/core/systemd/nm-sd-utils-core.h17
-rw-r--r--src/core/systemd/nm-sd-utils-dhcp.c41
-rw-r--r--src/core/systemd/nm-sd-utils-dhcp.h19
-rw-r--r--src/core/systemd/nm-sd.c113
-rw-r--r--src/core/systemd/nm-sd.h29
-rw-r--r--src/core/systemd/sd-adapt-core/condition.h5
-rw-r--r--src/core/systemd/sd-adapt-core/conf-parser.h3
-rw-r--r--src/core/systemd/sd-adapt-core/device-util.h3
-rw-r--r--src/core/systemd/sd-adapt-core/khash.h3
-rw-r--r--src/core/systemd/sd-adapt-core/network-util.h3
-rw-r--r--src/core/systemd/sd-adapt-core/nm-sd-adapt-core.c21
-rw-r--r--src/core/systemd/sd-adapt-core/nm-sd-adapt-core.h88
-rw-r--r--src/core/systemd/sd-adapt-core/sd-daemon.h3
-rw-r--r--src/core/systemd/sd-adapt-core/sd-device.h3
-rw-r--r--src/core/systemd/sd-adapt-core/udev-util.h3
-rw-r--r--src/core/systemd/src/libsystemd-network/arp-util.c140
-rw-r--r--src/core/systemd/src/libsystemd-network/arp-util.h18
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-identifier.c228
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-identifier.h62
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-internal.h69
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-lease-internal.h84
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-network.c246
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-option.c358
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-packet.c173
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp-protocol.h99
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp6-internal.h123
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp6-lease-internal.h63
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp6-network.c76
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp6-option.c795
-rw-r--r--src/core/systemd/src/libsystemd-network/dhcp6-protocol.h120
-rw-r--r--src/core/systemd/src/libsystemd-network/lldp-internal.h39
-rw-r--r--src/core/systemd/src/libsystemd-network/lldp-neighbor.c794
-rw-r--r--src/core/systemd/src/libsystemd-network/lldp-neighbor.h92
-rw-r--r--src/core/systemd/src/libsystemd-network/lldp-network.c80
-rw-r--r--src/core/systemd/src/libsystemd-network/lldp-network.h6
-rw-r--r--src/core/systemd/src/libsystemd-network/network-internal.c245
-rw-r--r--src/core/systemd/src/libsystemd-network/network-internal.h31
-rw-r--r--src/core/systemd/src/libsystemd-network/sd-dhcp-client.c2258
-rw-r--r--src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c1401
-rw-r--r--src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c1857
-rw-r--r--src/core/systemd/src/libsystemd-network/sd-dhcp6-lease.c435
-rw-r--r--src/core/systemd/src/libsystemd-network/sd-ipv4acd.c543
-rw-r--r--src/core/systemd/src/libsystemd-network/sd-ipv4ll.c342
-rw-r--r--src/core/systemd/src/libsystemd-network/sd-lldp.c500
-rw-r--r--src/core/systemd/src/libsystemd/sd-event/event-source.h220
-rw-r--r--src/core/systemd/src/libsystemd/sd-event/event-util.c101
-rw-r--r--src/core/systemd/src/libsystemd/sd-event/event-util.h13
-rw-r--r--src/core/systemd/src/libsystemd/sd-event/sd-event.c4329
-rw-r--r--src/core/systemd/src/libsystemd/sd-id128/id128-util.c218
-rw-r--r--src/core/systemd/src/libsystemd/sd-id128/id128-util.h38
-rw-r--r--src/core/systemd/src/libsystemd/sd-id128/sd-id128.c330
-rw-r--r--src/core/systemd/src/systemd/_sd-common.h102
-rw-r--r--src/core/systemd/src/systemd/sd-dhcp-client.h224
-rw-r--r--src/core/systemd/src/systemd/sd-dhcp-lease.h82
-rw-r--r--src/core/systemd/src/systemd/sd-dhcp-option.h38
-rw-r--r--src/core/systemd/src/systemd/sd-dhcp6-client.h180
-rw-r--r--src/core/systemd/src/systemd/sd-dhcp6-lease.h55
-rw-r--r--src/core/systemd/src/systemd/sd-dhcp6-option.h37
-rw-r--r--src/core/systemd/src/systemd/sd-event.h176
-rw-r--r--src/core/systemd/src/systemd/sd-id128.h124
-rw-r--r--src/core/systemd/src/systemd/sd-ipv4acd.h60
-rw-r--r--src/core/systemd/src/systemd/sd-ipv4ll.h62
-rw-r--r--src/core/systemd/src/systemd/sd-lldp.h194
-rw-r--r--src/core/systemd/src/systemd/sd-ndisc.h131
-rw-r--r--src/core/tests/config/test-config.c100
-rw-r--r--src/core/tests/meson.build12
-rw-r--r--src/core/tests/test-core-with-expect.c7
-rw-r--r--src/core/tests/test-core.c30
-rw-r--r--src/core/tests/test-ip4-config.c2
-rw-r--r--src/core/tests/test-ip6-config.c2
-rw-r--r--src/core/tests/test-l3cfg.c12
-rw-r--r--src/core/tests/test-systemd.c140
-rw-r--r--src/core/vpn/nm-vpn-connection.c78
-rw-r--r--src/core/vpn/nm-vpn-connection.h2
-rw-r--r--src/core/vpn/nm-vpn-manager.c2
538 files changed, 9036 insertions, 58913 deletions
diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c
index 54072210..7d3e80be 100644
--- a/src/core/NetworkManagerUtils.c
+++ b/src/core/NetworkManagerUtils.c
@@ -12,19 +12,23 @@
 #include <linux/pkt_sched.h>
 #include <linux/if_ether.h>
 
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-c-list.h"
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-base/nm-net-aux.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
-#include "nm-core-internal.h"
-#include "platform/nmp-object.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nmp-object.h"
 
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nm-linux-platform.h"
 #include "nm-auth-utils.h"
-#include "systemd/nm-sd-utils-shared.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
 /*****************************************************************************/
 
@@ -252,7 +256,7 @@ nm_utils_complete_generic(NMPlatform *         platform,
 
         g_object_set(G_OBJECT(s_con),
                      NM_SETTING_CONNECTION_UUID,
-                     nm_utils_uuid_generate_buf(uuid),
+                     nm_uuid_generate_random_str_arr(uuid),
                      NULL);
     }
 
@@ -902,10 +906,16 @@ nm_match_spec_device_by_pllink(const NMPlatformLink *pllink,
 NMPlatformRoutingRule *
 nm_ip_routing_rule_to_platform(const NMIPRoutingRule *rule, NMPlatformRoutingRule *out_pl)
 {
+    gboolean uid_range_has;
+    guint32  uid_range_start = 0;
+    guint32  uid_range_end   = 0;
+
     nm_assert(rule);
     nm_assert(nm_ip_routing_rule_validate(rule, NULL));
     nm_assert(out_pl);
 
+    uid_range_has = nm_ip_routing_rule_get_uid_range(rule, &uid_range_start, &uid_range_end);
+
     *out_pl = (NMPlatformRoutingRule){
         .addr_family = nm_ip_routing_rule_get_addr_family(rule),
         .flags       = (nm_ip_routing_rule_get_invert(rule) ? FIB_RULE_INVERT : 0),
@@ -932,6 +942,12 @@ nm_ip_routing_rule_to_platform(const NMIPRoutingRule *rule, NMPlatformRoutingRul
         .table   = nm_ip_routing_rule_get_table(rule),
         .suppress_prefixlen_inverse =
             ~((guint32) nm_ip_routing_rule_get_suppress_prefixlength(rule)),
+        .uid_range_has = uid_range_has,
+        .uid_range =
+            {
+                .start = uid_range_start,
+                .end   = uid_range_end,
+            },
     };
 
     nm_ip_routing_rule_get_xifname_bin(rule, TRUE, out_pl->iifname);
@@ -1293,9 +1309,9 @@ nm_utils_ip_route_attribute_to_platform(int                addr_family,
 
     if ((variant = nm_ip_route_get_attribute(s_route, NM_IP_ROUTE_ATTRIBUTE_TYPE))
         && g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) {
-        guint8 type;
+        int type;
 
-        type = nm_utils_route_type_by_name(g_variant_get_string(variant, NULL));
+        type = nm_net_aux_rtnl_rtntype_a2n(g_variant_get_string(variant, NULL));
         nm_assert(NM_IN_SET(type, RTN_UNICAST, RTN_LOCAL));
 
         r->type_coerced = nm_platform_route_type_coerce(type);
@@ -1482,12 +1498,7 @@ nm_utils_ip_addresses_to_dbus(int                          addr_family,
                         : (guint32) 0,
                 };
 
-                g_variant_builder_add(&builder_legacy,
-                                      "@au",
-                                      g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                                dbus_addr,
-                                                                3,
-                                                                sizeof(guint32)));
+                g_variant_builder_add(&builder_legacy, "@au", nm_g_variant_new_au(dbus_addr, 3));
             } else {
                 g_variant_builder_add(
                     &builder_legacy,
@@ -1609,12 +1620,7 @@ nm_utils_ip_routes_to_dbus(int                          addr_family,
                     r->r4.metric,
                 };
 
-                g_variant_builder_add(&builder_legacy,
-                                      "@au",
-                                      g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                                dbus_route,
-                                                                4,
-                                                                sizeof(guint32)));
+                g_variant_builder_add(&builder_legacy, "@au", nm_g_variant_new_au(dbus_route, 4));
             } else {
                 g_variant_builder_add(&builder_legacy,
                                       "(@ayu@ayu)",
@@ -1632,200 +1638,60 @@ nm_utils_ip_routes_to_dbus(int                          addr_family,
 
 /*****************************************************************************/
 
-typedef struct {
-    char *table;
-    char *rule;
-} ShareRule;
+/* Singleton NMPlatform subclass instance and cached class object */
+NM_DEFINE_SINGLETON_INSTANCE(NMPlatform);
 
-struct _NMUtilsShareRules {
-    GArray *rules;
-};
-
-static void
-_share_rule_clear(gpointer data)
-{
-    ShareRule *rule = data;
-
-    g_free(rule->table);
-    g_free(rule->rule);
-}
-
-NMUtilsShareRules *
-nm_utils_share_rules_new(void)
-{
-    NMUtilsShareRules *self;
-
-    self  = g_slice_new(NMUtilsShareRules);
-    *self = (NMUtilsShareRules){
-        .rules = g_array_sized_new(FALSE, FALSE, sizeof(ShareRule), 10),
-    };
-
-    g_array_set_clear_func(self->rules, _share_rule_clear);
-    return self;
-}
+NM_DEFINE_SINGLETON_REGISTER(NMPlatform);
 
+/**
+ * nm_platform_setup:
+ * @instance: the #NMPlatform instance
+ *
+ * Failing to set up #NMPlatform singleton results in a fatal error,
+ * as well as trying to initialize it multiple times without freeing
+ * it.
+ *
+ * NetworkManager will typically use only one platform object during
+ * its run. Test programs might want to switch platform implementations,
+ * though.
+ */
 void
-nm_utils_share_rules_free(NMUtilsShareRules *self)
+nm_platform_setup(NMPlatform *instance)
 {
-    if (!self)
-        return;
+    g_return_if_fail(NM_IS_PLATFORM(instance));
+    g_return_if_fail(!singleton_instance);
 
-    g_array_unref(self->rules);
-    nm_g_slice_free(self);
-}
-
-void
-nm_utils_share_rules_add_rule_take(NMUtilsShareRules *self, const char *table, char *rule_take)
-{
-    ShareRule *rule;
+    singleton_instance = instance;
 
-    g_return_if_fail(self);
-    g_return_if_fail(table);
-    g_return_if_fail(rule_take);
+    nm_singleton_instance_register();
 
-    rule  = nm_g_array_append_new(self->rules, ShareRule);
-    *rule = (ShareRule){
-        .table = g_strdup(table),
-        .rule  = g_steal_pointer(&rule_take),
-    };
+    nm_log_dbg(LOGD_CORE,
+               "setup %s singleton (" NM_HASH_OBFUSCATE_PTR_FMT ")",
+               "NMPlatform",
+               NM_HASH_OBFUSCATE_PTR(instance));
 }
 
-void
-nm_utils_share_rules_apply(NMUtilsShareRules *self, gboolean shared)
+/**
+ * nm_platform_get:
+ * @self: platform instance
+ *
+ * Retrieve #NMPlatform singleton. Use this whenever you want to connect to
+ * #NMPlatform signals. It is an error to call it before nm_platform_setup().
+ *
+ * Returns: (transfer none): The #NMPlatform singleton reference.
+ */
+NMPlatform *
+nm_platform_get()
 {
-    guint i;
-
-    g_return_if_fail(self);
+    g_assert(singleton_instance);
 
-    if (self->rules->len == 0)
-        return;
-
-    /* depending on whether we share or unshare, we add/remote the rules
-     * in opposite order. */
-    if (shared)
-        i = self->rules->len - 1;
-    else
-        i = 0;
-
-    for (;;) {
-        gs_free_error GError *error = NULL;
-        ShareRule *           rule;
-        gs_free const char ** argv = NULL;
-        gs_free char *        cmd  = NULL;
-        int                   status;
-
-        rule = &g_array_index(self->rules, ShareRule, i);
-
-        cmd  = g_strdup_printf("%s --table %s %s %s",
-                              IPTABLES_PATH,
-                              rule->table,
-                              shared ? "--insert" : "--delete",
-                              rule->rule);
-        argv = nm_utils_strsplit_set(cmd, " ");
-
-        nm_log_info(LOGD_SHARING, "Executing: %s", cmd);
-        if (!g_spawn_sync("/",
-                          (char **) argv,
-                          (char **) NM_PTRARRAY_EMPTY(const char *),
-                          G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
-                          NULL,
-                          NULL,
-                          NULL,
-                          NULL,
-                          &status,
-                          &error)) {
-            nm_log_warn(LOGD_SHARING, "Error executing command: %s", error->message);
-            goto next;
-        }
-        if (WEXITSTATUS(status)) {
-            nm_log_warn(LOGD_SHARING, "** Command returned exit status %d.", WEXITSTATUS(status));
-        }
-
-next:
-        if (shared) {
-            if (i == 0)
-                break;
-            i--;
-        } else {
-            i++;
-            if (i >= self->rules->len)
-                break;
-        }
-    }
+    return singleton_instance;
 }
 
+/*****************************************************************************/
+
 void
-nm_utils_share_rules_add_all_rules(NMUtilsShareRules *self,
-                                   const char *       ip_iface,
-                                   in_addr_t          addr,
-                                   guint              plen)
+nm_linux_platform_setup(void)
 {
-    in_addr_t netmask;
-    in_addr_t network;
-    char      str_mask[NM_UTILS_INET_ADDRSTRLEN];
-    char      str_addr[NM_UTILS_INET_ADDRSTRLEN];
-
-    nm_assert(self);
-
-    netmask = _nm_utils_ip4_prefix_to_netmask(plen);
-    _nm_utils_inet4_ntop(netmask, str_mask);
-
-    network = addr & netmask;
-    _nm_utils_inet4_ntop(network, str_addr);
-
-    nm_utils_share_rules_add_rule_v(
-        self,
-        "nat",
-        "POSTROUTING --source %s/%s ! --destination %s/%s --jump MASQUERADE",
-        str_addr,
-        str_mask,
-        str_addr,
-        str_mask);
-    nm_utils_share_rules_add_rule_v(
-        self,
-        "filter",
-        "FORWARD --destination %s/%s --out-interface %s --match state --state "
-        "ESTABLISHED,RELATED --jump ACCEPT",
-        str_addr,
-        str_mask,
-        ip_iface);
-    nm_utils_share_rules_add_rule_v(self,
-                                    "filter",
-                                    "FORWARD --source %s/%s --in-interface %s --jump ACCEPT",
-                                    str_addr,
-                                    str_mask,
-                                    ip_iface);
-    nm_utils_share_rules_add_rule_v(self,
-                                    "filter",
-                                    "FORWARD --in-interface %s --out-interface %s --jump ACCEPT",
-                                    ip_iface,
-                                    ip_iface);
-    nm_utils_share_rules_add_rule_v(self,
-                                    "filter",
-                                    "FORWARD --out-interface %s --jump REJECT",
-                                    ip_iface);
-    nm_utils_share_rules_add_rule_v(self,
-                                    "filter",
-                                    "FORWARD --in-interface %s --jump REJECT",
-                                    ip_iface);
-    nm_utils_share_rules_add_rule_v(
-        self,
-        "filter",
-        "INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT",
-        ip_iface);
-    nm_utils_share_rules_add_rule_v(
-        self,
-        "filter",
-        "INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT",
-        ip_iface);
-    nm_utils_share_rules_add_rule_v(
-        self,
-        "filter",
-        "INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT",
-        ip_iface);
-    nm_utils_share_rules_add_rule_v(
-        self,
-        "filter",
-        "INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT",
-        ip_iface);
+    nm_platform_setup(nm_linux_platform_new(FALSE, FALSE));
 }
diff --git a/src/core/NetworkManagerUtils.h b/src/core/NetworkManagerUtils.h
index 2afb5a3e..1c86387c 100644
--- a/src/core/NetworkManagerUtils.h
+++ b/src/core/NetworkManagerUtils.h
@@ -8,10 +8,10 @@
 #define __NETWORKMANAGER_UTILS_H__
 
 #include "nm-core-utils.h"
-#include "nm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
 #include "nm-setting-ip-config.h"
 #include "nm-setting-ip6-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 /*****************************************************************************/
 
@@ -197,18 +197,20 @@ nm_dhcp_lease_get_options(NMDhcpLease *lease)
     return (GHashTable *) lease;
 }
 
-static inline void
+static inline NMDhcpLease *
 nm_dhcp_lease_ref(NMDhcpLease *lease)
 {
     if (lease)
         g_hash_table_ref((GHashTable *) lease);
+    return lease;
 }
 
-static inline void
+static inline NMDhcpLease *
 nm_dhcp_lease_unref(NMDhcpLease *lease)
 {
     if (lease)
         g_hash_table_unref((GHashTable *) lease);
+    return NULL;
 }
 
 static inline const char *
@@ -224,30 +226,12 @@ NM_AUTO_DEFINE_FCN(NMDhcpLease *, _nm_auto_unref_dhcplease, nm_dhcp_lease_unref)
 
 /*****************************************************************************/
 
-typedef struct _NMUtilsShareRules NMUtilsShareRules;
-
-NMUtilsShareRules *nm_utils_share_rules_new(void);
-
-void nm_utils_share_rules_free(NMUtilsShareRules *self);
-
-void
-nm_utils_share_rules_add_rule_take(NMUtilsShareRules *self, const char *table, char *rule_take);
-
-static inline void
-nm_utils_share_rules_add_rule(NMUtilsShareRules *self, const char *table, const char *rule)
-{
-    nm_utils_share_rules_add_rule_take(self, table, g_strdup(rule));
-}
-
-#define nm_utils_share_rules_add_rule_v(self, table, ...) \
-    nm_utils_share_rules_add_rule_take((self), (table), g_strdup_printf(__VA_ARGS__))
+void        nm_platform_setup(NMPlatform *instance);
+NMPlatform *nm_platform_get(void);
 
-void nm_utils_share_rules_add_all_rules(NMUtilsShareRules *self,
-                                        const char *       ip_iface,
-                                        in_addr_t          addr,
-                                        guint              plen);
+#define NM_PLATFORM_GET (nm_platform_get())
 
-void nm_utils_share_rules_apply(NMUtilsShareRules *self, gboolean shared);
+void nm_linux_platform_setup(void);
 
 /*****************************************************************************/
 
diff --git a/src/core/devices/adsl/meson.build b/src/core/devices/adsl/meson.build
index 95f61d95..ef87c6e8 100644
--- a/src/core/devices/adsl/meson.build
+++ b/src/core/devices/adsl/meson.build
@@ -7,7 +7,6 @@ libnm_device_plugin_adsl = shared_module(
     'nm-device-adsl.c',
   ),
   dependencies: core_plugin_dep,
-  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
diff --git a/src/core/devices/adsl/nm-atm-manager.c b/src/core/devices/adsl/nm-atm-manager.c
index 9be9b5ce..bd9ed8cc 100644
--- a/src/core/devices/adsl/nm-atm-manager.c
+++ b/src/core/devices/adsl/nm-atm-manager.c
@@ -11,8 +11,8 @@
 #include "nm-setting-adsl.h"
 #include "nm-device-adsl.h"
 #include "devices/nm-device-factory.h"
-#include "platform/nm-platform.h"
-#include "nm-udev-aux/nm-udev-utils.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-udev-aux/nm-udev-utils.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/devices/adsl/nm-device-adsl.c b/src/core/devices/adsl/nm-device-adsl.c
index 34c062a8..4e4c9dcc 100644
--- a/src/core/devices/adsl/nm-device-adsl.c
+++ b/src/core/devices/adsl/nm-device-adsl.c
@@ -17,7 +17,7 @@
 
 #include "nm-ip4-config.h"
 #include "devices/nm-device-private.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "ppp/nm-ppp-manager-call.h"
 #include "ppp/nm-ppp-status.h"
 #include "nm-setting-adsl.h"
@@ -265,7 +265,7 @@ pppoe_vcc_config(NMDeviceAdsl *self)
     _LOGD(LOGD_ADSL, "ATM setup successful");
 
     /* otherwise we're good for stage3 */
-    nm_platform_link_set_up(nm_device_get_platform(device), priv->nas_ifindex, NULL);
+    nm_platform_link_change_flags(nm_device_get_platform(device), priv->nas_ifindex, IFF_UP, TRUE);
 
     return TRUE;
 }
@@ -674,12 +674,8 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_adsl = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_ADSL,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Carrier",
-                                                             "b",
-                                                             NM_DEVICE_CARRIER), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Carrier", "b", NM_DEVICE_CARRIER), ), ),
 };
 
 static void
diff --git a/src/core/devices/bluetooth/meson.build b/src/core/devices/bluetooth/meson.build
index d5f26068..5b196d08 100644
--- a/src/core/devices/bluetooth/meson.build
+++ b/src/core/devices/bluetooth/meson.build
@@ -8,11 +8,11 @@ libnm_device_plugin_bluetooth_static = static_library(
     'nm-device-bt.c',
   ) + (enable_bluez5_dun ? files('nm-bluez5-dun.c') : files()),
   dependencies: [
+    libnm_core_public_dep,
     core_default_dep,
     libnm_wwan_dep,
     bluez5_dep,
   ],
-  c_args: daemon_c_flags,
 )
 
 libnm_device_plugin_bluetooth_static_dep = declare_dependency(
diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c
index dd998d29..fc081580 100644
--- a/src/core/devices/bluetooth/nm-bluez-manager.c
+++ b/src/core/devices/bluetooth/nm-bluez-manager.c
@@ -12,8 +12,9 @@
 #include <gmodule.h>
 #include <linux/if_ether.h>
 
-#include "nm-glib-aux/nm-dbus-aux.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-dbus-manager.h"
 #include "devices/nm-device-factory.h"
 #include "devices/nm-device-bridge.h"
@@ -23,9 +24,9 @@
 #include "nm-device-bt.h"
 #include "nm-manager.h"
 #include "nm-bluez5-dun.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 
 /*****************************************************************************/
 
@@ -574,11 +575,12 @@ _bzobj_to_string(const BzDBusObj *bzobj, char *buf, gsize len)
 #define _LOG_bzobj(bzobj, context)                           \
     G_STMT_START                                             \
     {                                                        \
-        const BzDBusObj *const _bzobj = (bzobj);             \
+        const BzDBusObj *const _bzobj   = (bzobj);           \
+        const char *const      _context = (context);         \
         char                   _buf[500];                    \
                                                              \
         _LOGT("change %-21s %s : { %s }",                    \
-              (context),                                     \
+              _context,                                      \
               _bzobj->object_path,                           \
               _bzobj_to_string(_bzobj, _buf, sizeof(_buf))); \
     }                                                        \
@@ -1321,7 +1323,7 @@ _conn_create_panu_connection(NMBluezManager *self, BzDBusObj *bzobj)
     char                          uuid[37];
     gs_free_error GError *error = NULL;
 
-    nm_utils_uuid_generate_buf(uuid);
+    nm_uuid_generate_random_str_arr(uuid);
     id = g_strdup_printf(_("%s Network"), bzobj->d_device.name);
 
     connection = nm_simple_connection_new();
@@ -2879,6 +2881,8 @@ dispose(GObject *object)
     g_clear_object(&priv->dbus_connection);
 
     nm_clear_pointer(&priv->bzobjs, g_hash_table_destroy);
+    nm_clear_pointer(&priv->conn_data_heads, g_hash_table_destroy);
+    nm_clear_pointer(&priv->conn_data_elems, g_hash_table_destroy);
 }
 
 static void
diff --git a/src/core/devices/bluetooth/nm-device-bt.c b/src/core/devices/bluetooth/nm-device-bt.c
index c07be2d3..8f4ceba7 100644
--- a/src/core/devices/bluetooth/nm-device-bt.c
+++ b/src/core/devices/bluetooth/nm-device-bt.c
@@ -10,7 +10,7 @@
 #include <stdio.h>
 #include <linux/if_ether.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-bluez-common.h"
 #include "nm-bluez-manager.h"
 #include "devices/nm-device-private.h"
@@ -26,7 +26,7 @@
 #include "nm-utils.h"
 #include "nm-bt-error.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #include "devices/wwan/nm-modem-manager.h"
 #include "devices/wwan/nm-modem.h"
@@ -1317,16 +1317,12 @@ finalize(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_bluetooth = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_BLUETOOTH,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Name", "s", NM_DEVICE_BT_NAME),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("BtCapabilities",
-                                                             "u",
-                                                             NM_DEVICE_BT_CAPABILITIES), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Name", "s", NM_DEVICE_BT_NAME),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("BtCapabilities",
+                                                           "u",
+                                                           NM_DEVICE_BT_CAPABILITIES), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-acd-manager.c b/src/core/devices/nm-acd-manager.c
index b95f90fd..995abacd 100644
--- a/src/core/devices/nm-acd-manager.c
+++ b/src/core/devices/nm-acd-manager.c
@@ -12,7 +12,7 @@
 #include <sys/wait.h>
 #include <linux/if_ether.h>
 
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "n-acd/src/n-acd.h"
diff --git a/src/core/devices/nm-device-6lowpan.c b/src/core/devices/nm-device-6lowpan.c
index fe116dd4..96218658 100644
--- a/src/core/devices/nm-device-6lowpan.c
+++ b/src/core/devices/nm-device-6lowpan.c
@@ -9,7 +9,7 @@
 
 #include "nm-device-private.h"
 #include "settings/nm-settings.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-6lowpan.h"
 #include "nm-utils.h"
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index f68c080b..3b3b8fcd 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -12,9 +12,9 @@
 
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-ip4-config.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceBond
@@ -109,6 +109,24 @@ _set_bond_attr(NMDevice *device, const char *attr, const char *value)
     int           ifindex = nm_device_get_ifindex(device);
     gboolean      ret;
 
+    nm_assert(attr && attr[0]);
+    nm_assert(value);
+
+    if (nm_streq(value, NM_BOND_AD_ACTOR_SYSTEM_DEFAULT)
+        && nm_streq(attr, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)) {
+        gs_free char *cur_val = NULL;
+
+        /* kernel does not allow setting ad_actor_system to "00:00:00:00:00:00". We would thus
+         * log an EINVAL error. Avoid that... at least, if the value is already "00:00:00:00:00:00". */
+        cur_val =
+            nm_platform_sysctl_master_get_option(nm_device_get_platform(device), ifindex, attr);
+        if (nm_streq0(cur_val, NM_BOND_AD_ACTOR_SYSTEM_DEFAULT))
+            return TRUE;
+
+        /* OK, the current value is different, and we will proceed setting "00:00:00:00:00:00".
+         * That will fail, and we will log a warning. There is nothing else to do. */
+    }
+
     ret =
         nm_platform_sysctl_master_set_option(nm_device_get_platform(device), ifindex, attr, value);
     if (!ret)
@@ -426,9 +444,10 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure)
         _LOGD(LOGD_BOND, "bond slave %s is already released", nm_device_get_ip_iface(slave));
 
     if (configure) {
-        /* When the last slave is released the bond MAC will be set to a random
-         * value by kernel; remember the current one and restore it afterwards.
-         */
+        NMConnection *  applied;
+        NMSettingWired *s_wired;
+        const char *    cloned_mac;
+
         address = g_strdup(nm_device_get_hw_address(device));
 
         if (ifindex_slave > 0) {
@@ -443,9 +462,16 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure)
             }
         }
 
-        nm_platform_process_events(nm_device_get_platform(device));
-        if (nm_device_update_hw_address(device))
-            nm_device_hw_addr_set(device, address, "restore", FALSE);
+        if ((applied = nm_device_get_applied_connection(device))
+            && ((s_wired = nm_connection_get_setting_wired(applied)))
+            && ((cloned_mac = nm_setting_wired_get_cloned_mac_address(s_wired)))) {
+            /* When the last slave is released the bond MAC will be set to a random
+             * value by kernel; if we have set a cloned-mac-address, we need to
+             * restore it to the previous value. */
+            nm_platform_process_events(nm_device_get_platform(device));
+            if (nm_device_update_hw_address(device))
+                nm_device_hw_addr_set(device, address, "restore", FALSE);
+        }
 
         /* Kernel bonding code "closes" the slave when releasing it, (which clears
          * IFF_UP), so we must bring it back up here to ensure carrier changes and
@@ -575,16 +601,10 @@ nm_device_bond_init(NMDeviceBond *self)
 static const NMDBusInterfaceInfoExtended interface_info_device_bond = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_BOND,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Carrier", "b", NM_DEVICE_CARRIER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Slaves",
-                                                             "ao",
-                                                             NM_DEVICE_SLAVES), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Carrier", "b", NM_DEVICE_CARRIER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Slaves", "ao", NM_DEVICE_SLAVES), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index c919d85d..a095dc3e 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -12,9 +12,9 @@
 
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceBridge
 #include "nm-device-logging.h"
@@ -1033,7 +1033,7 @@ create_and_realize(NMDevice *             device,
     const char *        iface = nm_device_get_iface(device);
     const char *        hwaddr;
     gs_free char *      hwaddr_cloned = NULL;
-    guint8              mac_address[NM_UTILS_HWADDR_LEN_MAX];
+    guint8              mac_address[_NM_UTILS_HWADDR_LEN_MAX];
     NMPlatformLnkBridge props;
     int                 r;
     guint32             mtu = 0;
@@ -1142,16 +1142,10 @@ nm_device_bridge_init(NMDeviceBridge *self)
 static const NMDBusInterfaceInfoExtended interface_info_device_bridge = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_BRIDGE,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Carrier", "b", NM_DEVICE_CARRIER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Slaves",
-                                                             "ao",
-                                                             NM_DEVICE_SLAVES), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Carrier", "b", NM_DEVICE_CARRIER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Slaves", "ao", NM_DEVICE_SLAVES), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-dummy.c b/src/core/devices/nm-device-dummy.c
index 13cfd3b0..488f0ba0 100644
--- a/src/core/devices/nm-device-dummy.c
+++ b/src/core/devices/nm-device-dummy.c
@@ -13,10 +13,10 @@
 #include "nm-act-request.h"
 #include "nm-device-private.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-dummy.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceDummy
 #include "nm-device-logging.h"
@@ -121,12 +121,10 @@ nm_device_dummy_init(NMDeviceDummy *self)
 static const NMDBusInterfaceInfoExtended interface_info_device_dummy = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_DUMMY,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress",
+                                                           "s",
+                                                           NM_DEVICE_HW_ADDRESS), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index 44428869..305552f0 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -14,6 +14,7 @@
 #include <libudev.h>
 #include <linux/if_ether.h>
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "nm-device-private.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
@@ -24,17 +25,17 @@
 #include "ppp/nm-ppp-manager.h"
 #include "ppp/nm-ppp-manager-call.h"
 #include "ppp/nm-ppp-status.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nm-platform-utils.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
 #include "nm-dcb.h"
 #include "settings/nm-settings-connection.h"
 #include "nm-config.h"
 #include "nm-device-ethernet-utils.h"
 #include "settings/nm-settings.h"
 #include "nm-device-factory.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
-#include "nm-udev-aux/nm-udev-utils.h"
+#include "libnm-udev-aux/nm-udev-utils.h"
 #include "nm-device-veth.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceEthernet
@@ -730,7 +731,9 @@ supplicant_iface_state_cb(NMSupplicantInterface *iface,
 
     if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
         supplicant_interface_release(self);
-        wired_auth_cond_fail(self, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+        nm_device_state_changed(NM_DEVICE(self),
+                                NM_DEVICE_STATE_FAILED,
+                                NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
         return;
     }
 
@@ -1716,11 +1719,11 @@ new_default_connection(NMDevice *self)
 
     /* Create a stable UUID. The UUID is also the Network_ID for stable-privacy addr-gen-mode,
      * thus when it changes we will also generate different IPv6 addresses. */
-    uuid = _nm_utils_uuid_generate_from_strings("default-wired",
-                                                nm_utils_machine_id_str(),
-                                                defname,
-                                                perm_hw_addr ?: iface,
-                                                NULL);
+    uuid = nm_uuid_generate_from_strings("default-wired",
+                                         nm_utils_machine_id_str(),
+                                         defname,
+                                         perm_hw_addr ?: iface,
+                                         NULL);
 
     g_object_set(setting,
                  NM_SETTING_CONNECTION_ID,
@@ -1745,7 +1748,7 @@ new_default_connection(NMDevice *self)
     if (dev)
         uprop = udev_device_get_property_value(dev, "NM_AUTO_DEFAULT_LINK_LOCAL_ONLY");
 
-    if (nm_udev_utils_property_as_boolean(uprop)) {
+    if (_nm_utils_ascii_str_to_bool(uprop, FALSE)) {
         setting = nm_setting_ip4_config_new();
         g_object_set(setting,
                      NM_SETTING_IP_CONFIG_METHOD,
@@ -1784,7 +1787,8 @@ update_connection(NMDevice *device, NMConnection *connection)
     const char *             mac      = nm_device_get_hw_address(device);
     const char *             mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
     GHashTableIter           iter;
-    gpointer                 key, value;
+    const char *             key;
+    const char *             value;
 
     if (!s_wired) {
         s_wired = (NMSettingWired *) nm_setting_wired_new();
@@ -1822,8 +1826,8 @@ update_connection(NMDevice *device, NMConnection *connection)
 
     _nm_setting_wired_clear_s390_options(s_wired);
     g_hash_table_iter_init(&iter, priv->s390_options);
-    while (g_hash_table_iter_next(&iter, &key, &value))
-        nm_setting_wired_add_s390_option(s_wired, (const char *) key, (const char *) value);
+    while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value))
+        nm_setting_wired_add_s390_option(s_wired, key, value);
 }
 
 static void
@@ -1998,24 +2002,16 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 static const NMDBusInterfaceInfoExtended interface_info_device_wired = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_WIRED,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("PermHwAddress",
-                                                             "s",
-                                                             NM_DEVICE_PERM_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Speed",
-                                                             "u",
-                                                             NM_DEVICE_ETHERNET_SPEED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("S390Subchannels",
-                                                             "as",
-                                                             NM_DEVICE_ETHERNET_S390_SUBCHANNELS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Carrier",
-                                                             "b",
-                                                             NM_DEVICE_CARRIER), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("PermHwAddress",
+                                                           "s",
+                                                           NM_DEVICE_PERM_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Speed", "u", NM_DEVICE_ETHERNET_SPEED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("S390Subchannels",
+                                                           "as",
+                                                           NM_DEVICE_ETHERNET_S390_SUBCHANNELS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Carrier", "b", NM_DEVICE_CARRIER), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c
index 81124a8d..bdc64135 100644
--- a/src/core/devices/nm-device-factory.c
+++ b/src/core/devices/nm-device-factory.c
@@ -11,9 +11,9 @@
 #include <sys/stat.h>
 #include <gmodule.h>
 
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-setting-bluetooth.h"
 
 #define PLUGIN_PREFIX "libnm-device-plugin-"
diff --git a/src/core/devices/nm-device-generic.c b/src/core/devices/nm-device-generic.c
index a319666a..08ea5ac5 100644
--- a/src/core/devices/nm-device-generic.c
+++ b/src/core/devices/nm-device-generic.c
@@ -8,8 +8,8 @@
 #include "nm-device-generic.h"
 
 #include "nm-device-private.h"
-#include "platform/nm-platform.h"
-#include "nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 /*****************************************************************************/
 
@@ -191,16 +191,12 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_generic = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_GENERIC,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(
                 "TypeDescription",
                 "s",
                 NM_DEVICE_GENERIC_TYPE_DESCRIPTION), ), ),
-    .legacy_property_changed = TRUE,
 };
 
 static void
diff --git a/src/core/devices/nm-device-infiniband.c b/src/core/devices/nm-device-infiniband.c
index f54ffcf0..37065bc8 100644
--- a/src/core/devices/nm-device-infiniband.c
+++ b/src/core/devices/nm-device-infiniband.c
@@ -14,9 +14,9 @@
 #include "nm-device-private.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define NM_DEVICE_INFINIBAND_IS_PARTITION "is-partition"
 
@@ -366,15 +366,9 @@ nm_device_infiniband_init(NMDeviceInfiniband *self)
 static const NMDBusInterfaceInfoExtended interface_info_device_infiniband = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_INFINIBAND,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Carrier",
-                                                             "b",
-                                                             NM_DEVICE_CARRIER), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Carrier", "b", NM_DEVICE_CARRIER), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-ip-tunnel.c b/src/core/devices/nm-device-ip-tunnel.c
index da6afb3b..c55dd9f6 100644
--- a/src/core/devices/nm-device-ip-tunnel.c
+++ b/src/core/devices/nm-device-ip-tunnel.c
@@ -16,9 +16,9 @@
 
 #include "nm-device-private.h"
 #include "nm-manager.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "settings/nm-settings.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
@@ -1050,39 +1050,33 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_ip_tunnel = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "u", NM_DEVICE_IP_TUNNEL_MODE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Parent", "o", NM_DEVICE_PARENT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Local",
-                                                             "s",
-                                                             NM_DEVICE_IP_TUNNEL_LOCAL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Remote",
-                                                             "s",
-                                                             NM_DEVICE_IP_TUNNEL_REMOTE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ttl", "y", NM_DEVICE_IP_TUNNEL_TTL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Tos", "y", NM_DEVICE_IP_TUNNEL_TOS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(
-                "PathMtuDiscovery",
-                "b",
-                NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("InputKey",
-                                                             "s",
-                                                             NM_DEVICE_IP_TUNNEL_INPUT_KEY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("OutputKey",
-                                                             "s",
-                                                             NM_DEVICE_IP_TUNNEL_OUTPUT_KEY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(
-                "EncapsulationLimit",
-                "y",
-                NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("FlowLabel",
-                                                             "u",
-                                                             NM_DEVICE_IP_TUNNEL_FLOW_LABEL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Flags",
-                                                             "u",
-                                                             NM_DEVICE_IP_TUNNEL_FLAGS), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "u", NM_DEVICE_IP_TUNNEL_MODE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Parent", "o", NM_DEVICE_PARENT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Local", "s", NM_DEVICE_IP_TUNNEL_LOCAL),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Remote",
+                                                           "s",
+                                                           NM_DEVICE_IP_TUNNEL_REMOTE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ttl", "y", NM_DEVICE_IP_TUNNEL_TTL),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Tos", "y", NM_DEVICE_IP_TUNNEL_TOS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("PathMtuDiscovery",
+                                                           "b",
+                                                           NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("InputKey",
+                                                           "s",
+                                                           NM_DEVICE_IP_TUNNEL_INPUT_KEY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("OutputKey",
+                                                           "s",
+                                                           NM_DEVICE_IP_TUNNEL_OUTPUT_KEY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("EncapsulationLimit",
+                                                           "y",
+                                                           NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FlowLabel",
+                                                           "u",
+                                                           NM_DEVICE_IP_TUNNEL_FLOW_LABEL),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Flags",
+                                                           "u",
+                                                           NM_DEVICE_IP_TUNNEL_FLAGS), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-macsec.c b/src/core/devices/nm-device-macsec.c
index 51b820a1..51b7225b 100644
--- a/src/core/devices/nm-device-macsec.c
+++ b/src/core/devices/nm-device-macsec.c
@@ -11,11 +11,11 @@
 
 #include "nm-act-request.h"
 #include "nm-device-private.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-manager.h"
 #include "nm-setting-macsec.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "supplicant/nm-supplicant-manager.h"
 #include "supplicant/nm-supplicant-interface.h"
 #include "supplicant/nm-supplicant-config.h"
@@ -870,40 +870,36 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_macsec = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_MACSEC,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Parent", "o", NM_DEVICE_PARENT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Sci", "t", NM_DEVICE_MACSEC_SCI),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("IcvLength",
-                                                             "y",
-                                                             NM_DEVICE_MACSEC_ICV_LENGTH),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("CipherSuite",
-                                                             "t",
-                                                             NM_DEVICE_MACSEC_CIPHER_SUITE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Window",
-                                                             "u",
-                                                             NM_DEVICE_MACSEC_WINDOW),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("EncodingSa",
-                                                             "y",
-                                                             NM_DEVICE_MACSEC_ENCODING_SA),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Validation",
-                                                             "s",
-                                                             NM_DEVICE_MACSEC_VALIDATION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Encrypt",
-                                                             "b",
-                                                             NM_DEVICE_MACSEC_ENCRYPT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Protect",
-                                                             "b",
-                                                             NM_DEVICE_MACSEC_PROTECT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("IncludeSci",
-                                                             "b",
-                                                             NM_DEVICE_MACSEC_INCLUDE_SCI),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Es", "b", NM_DEVICE_MACSEC_ES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Scb", "b", NM_DEVICE_MACSEC_SCB),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ReplayProtect",
-                                                             "b",
-                                                             NM_DEVICE_MACSEC_REPLAY_PROTECT), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Parent", "o", NM_DEVICE_PARENT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Sci", "t", NM_DEVICE_MACSEC_SCI),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("IcvLength",
+                                                           "y",
+                                                           NM_DEVICE_MACSEC_ICV_LENGTH),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("CipherSuite",
+                                                           "t",
+                                                           NM_DEVICE_MACSEC_CIPHER_SUITE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Window", "u", NM_DEVICE_MACSEC_WINDOW),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("EncodingSa",
+                                                           "y",
+                                                           NM_DEVICE_MACSEC_ENCODING_SA),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Validation",
+                                                           "s",
+                                                           NM_DEVICE_MACSEC_VALIDATION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Encrypt",
+                                                           "b",
+                                                           NM_DEVICE_MACSEC_ENCRYPT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Protect",
+                                                           "b",
+                                                           NM_DEVICE_MACSEC_PROTECT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("IncludeSci",
+                                                           "b",
+                                                           NM_DEVICE_MACSEC_INCLUDE_SCI),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Es", "b", NM_DEVICE_MACSEC_ES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Scb", "b", NM_DEVICE_MACSEC_SCB),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ReplayProtect",
+                                                           "b",
+                                                           NM_DEVICE_MACSEC_REPLAY_PROTECT), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-macvlan.c b/src/core/devices/nm-device-macvlan.c
index e8b39ed6..7302ecdf 100644
--- a/src/core/devices/nm-device-macvlan.c
+++ b/src/core/devices/nm-device-macvlan.c
@@ -13,7 +13,7 @@
 #include "settings/nm-settings.h"
 #include "nm-act-request.h"
 #include "nm-manager.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-macvlan.h"
 #include "nm-setting-wired.h"
@@ -502,17 +502,13 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_macvlan = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_MACVLAN,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Parent", "o", NM_DEVICE_PARENT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "s", NM_DEVICE_MACVLAN_MODE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("NoPromisc",
-                                                             "b",
-                                                             NM_DEVICE_MACVLAN_NO_PROMISC),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Tab",
-                                                             "b",
-                                                             NM_DEVICE_MACVLAN_TAP), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Parent", "o", NM_DEVICE_PARENT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "s", NM_DEVICE_MACVLAN_MODE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NoPromisc",
+                                                           "b",
+                                                           NM_DEVICE_MACVLAN_NO_PROMISC),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Tab", "b", NM_DEVICE_MACVLAN_TAP), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-ppp.c b/src/core/devices/nm-device-ppp.c
index 4040f2d3..8df245bb 100644
--- a/src/core/devices/nm-device-ppp.c
+++ b/src/core/devices/nm-device-ppp.c
@@ -13,7 +13,7 @@
 #include "nm-device-private.h"
 #include "nm-manager.h"
 #include "nm-setting-pppoe.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "ppp/nm-ppp-manager.h"
 #include "ppp/nm-ppp-manager-call.h"
 #include "ppp/nm-ppp-status.h"
@@ -274,10 +274,7 @@ dispose(GObject *object)
 }
 
 static const NMDBusInterfaceInfoExtended interface_info_device_ppp = {
-    .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
-        NM_DBUS_INTERFACE_DEVICE_PPP,
-        .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ), ),
-    .legacy_property_changed = TRUE,
+    .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(NM_DBUS_INTERFACE_DEVICE_PPP, ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h
index 8675a699..eb37b14f 100644
--- a/src/core/devices/nm-device-private.h
+++ b/src/core/devices/nm-device-private.h
@@ -17,14 +17,6 @@ typedef enum {
     NM_DEVICE_STAGE_STATE_COMPLETED = 2,
 } NMDeviceStageState;
 
-typedef enum {
-    NM_DEVICE_IP_STATE_NONE,
-    NM_DEVICE_IP_STATE_WAIT,
-    NM_DEVICE_IP_STATE_CONF,
-    NM_DEVICE_IP_STATE_DONE,
-    NM_DEVICE_IP_STATE_FAIL,
-} NMDeviceIPState;
-
 enum NMActStageReturn {
     NM_ACT_STAGE_RETURN_FAILURE = 0, /* Hard failure of activation */
     NM_ACT_STAGE_RETURN_SUCCESS,     /* Activation stage done */
@@ -117,8 +109,6 @@ nm_device_activate_ip6_state_done(NMDevice *self)
     return nm_device_activate_get_ip_state(self, AF_INET6) == NM_DEVICE_IP_STATE_DONE;
 }
 
-void nm_device_set_dhcp_anycast_address(NMDevice *device, const char *addr);
-
 gboolean nm_device_dhcp4_renew(NMDevice *device, gboolean release);
 gboolean nm_device_dhcp6_renew(NMDevice *device, gboolean release);
 
diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c
index edca69e9..ebccddfc 100644
--- a/src/core/devices/nm-device-tun.c
+++ b/src/core/devices/nm-device-tun.c
@@ -14,10 +14,10 @@
 #include "nm-act-request.h"
 #include "nm-device-private.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-tun.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceTun
 #include "nm-device-logging.h"
@@ -444,22 +444,18 @@ nm_device_tun_init(NMDeviceTun *self)
 static const NMDBusInterfaceInfoExtended interface_info_device_tun = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_TUN,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Owner", "x", NM_DEVICE_TUN_OWNER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Group", "x", NM_DEVICE_TUN_GROUP),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "s", NM_DEVICE_TUN_MODE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("NoPi", "b", NM_DEVICE_TUN_NO_PI),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("VnetHdr",
-                                                             "b",
-                                                             NM_DEVICE_TUN_VNET_HDR),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("MultiQueue",
-                                                             "b",
-                                                             NM_DEVICE_TUN_MULTI_QUEUE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Owner", "x", NM_DEVICE_TUN_OWNER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Group", "x", NM_DEVICE_TUN_GROUP),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "s", NM_DEVICE_TUN_MODE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NoPi", "b", NM_DEVICE_TUN_NO_PI),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("VnetHdr", "b", NM_DEVICE_TUN_VNET_HDR),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("MultiQueue",
+                                                           "b",
+                                                           NM_DEVICE_TUN_MULTI_QUEUE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress",
+                                                           "s",
+                                                           NM_DEVICE_HW_ADDRESS), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c
new file mode 100644
index 00000000..f40ca570
--- /dev/null
+++ b/src/core/devices/nm-device-utils.c
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "src/core/nm-default-daemon.h"
+#include "src/core/dns/nm-dns-manager.h"
+#include "src/core/dns/nm-dns-systemd-resolved.h"
+
+#include "nm-device-utils.h"
+#include "nm-core-utils.h"
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE(
+    nm_device_state_queued_state_to_str,
+    NMDeviceState,
+    NM_UTILS_LOOKUP_DEFAULT(NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "???"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_UNKNOWN,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "unknown"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_UNMANAGED,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "unmanaged"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_UNAVAILABLE,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "unavailable"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_DISCONNECTED,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "disconnected"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_PREPARE,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "prepare"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_CONFIG,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "config"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_NEED_AUTH,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "need-auth"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_IP_CONFIG,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "ip-config"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_IP_CHECK,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "ip-check"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_SECONDARIES,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "secondaries"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_ACTIVATED,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "activated"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_DEACTIVATING,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "deactivating"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_FAILED,
+                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "failed"), );
+
+const char *
+nm_device_state_to_str(NMDeviceState state)
+{
+    return nm_device_state_queued_state_to_str(state)
+           + NM_STRLEN(NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE);
+}
+
+NM_UTILS_LOOKUP_STR_DEFINE(
+    nm_device_state_reason_to_str,
+    NMDeviceStateReason,
+    NM_UTILS_LOOKUP_DEFAULT(NULL),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNKNOWN, "unknown"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NONE, "none"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NOW_MANAGED, "managed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NOW_UNMANAGED, "unmanaged"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CONFIG_FAILED, "config-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE, "ip-config-unavailable"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED, "ip-config-expired"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NO_SECRETS, "no-secrets"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT, "supplicant-disconnect"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED,
+                             "supplicant-config-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED, "supplicant-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT, "supplicant-timeout"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PPP_START_FAILED, "ppp-start-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PPP_DISCONNECT, "ppp-disconnect"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PPP_FAILED, "ppp-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DHCP_START_FAILED, "dhcp-start-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DHCP_ERROR, "dhcp-error"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DHCP_FAILED, "dhcp-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SHARED_START_FAILED, "sharing-start-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SHARED_FAILED, "sharing-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED, "autoip-start-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_ERROR, "autoip-error"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_FAILED, "autoip-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_BUSY, "modem-busy"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE, "modem-no-dialtone"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER, "modem-no-carrier"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT, "modem-dial-timeout"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED, "modem-dial-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED, "modem-init-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_APN_FAILED, "gsm-apn-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING,
+                             "gsm-registration-idle"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED,
+                             "gsm-registration-denied"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT,
+                             "gsm-registration-timeout"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED,
+                             "gsm-registration-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED, "gsm-pin-check-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_FIRMWARE_MISSING, "firmware-missing"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_REMOVED, "removed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SLEEPING, "sleeping"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CONNECTION_REMOVED, "connection-removed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_USER_REQUESTED, "user-requested"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CARRIER, "carrier-changed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED, "connection-assumed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, "supplicant-available"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND, "modem-not-found"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_BT_FAILED, "bluetooth-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED, "gsm-sim-not-inserted"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED, "gsm-sim-pin-required"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED, "gsm-sim-puk-required"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_WRONG, "gsm-sim-wrong"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_INFINIBAND_MODE, "infiniband-mode"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED, "dependency-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_BR2684_FAILED, "br2684-bridge-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE,
+                             "modem-manager-unavailable"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SSID_NOT_FOUND, "ssid-not-found"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED,
+                             "secondary-connection-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED, "dcb-fcoe-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED, "teamd-control-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_FAILED, "modem-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_AVAILABLE, "modem-available"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT, "sim-pin-incorrect"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NEW_ACTIVATION, "new-activation"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PARENT_CHANGED, "parent-changed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED,
+                             "parent-managed-changed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE, "ip-address-duplicate"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, "ip-method-unsupported"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED,
+                             "sriov-configuration-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, "peer-not-found"), );
+
+NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_str,
+                           NMDeviceMtuSource,
+                           NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_NONE, "none"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_PARENT, "parent"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_IP_CONFIG, "ip-config"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_CONNECTION,
+                                                    "connection"), );
+
+NM_UTILS_LOOKUP_STR_DEFINE(nm_device_sys_iface_state_to_str,
+                           NMDeviceSysIfaceState,
+                           NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_EXTERNAL, "external"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_ASSUME, "assume"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_MANAGED, "managed"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_REMOVED,
+                                                    "removed"), );
+
+NM_UTILS_LOOKUP_STR_DEFINE(nm_device_ip_state_to_str,
+                           NMDeviceIPState,
+                           NM_UTILS_LOOKUP_DEFAULT_WARN("unknown"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_NONE, "none"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_WAIT, "wait"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_CONF, "conf"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_DONE, "done"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_FAIL, "fail"), );
+
+/*****************************************************************************/
+
+#define SD_RESOLVED_DNS (1UL << 0)
+/* Don't answer request from locally synthesized records (which includes /etc/hosts) */
+#define SD_RESOLVED_NO_SYNTHESIZE (1UL << 11)
+
+typedef struct {
+    int                                addr_family;
+    NMIPAddr                           address;
+    gulong                             cancellable_id;
+    GTask *                            task;
+    NMDnsSystemdResolvedResolveHandle *resolved_handle;
+} ResolveAddrInfo;
+
+#define _NMLOG_PREFIX_NAME "resolve-addr"
+#define _NMLOG_DOMAIN      LOGD_CORE
+#define _NMLOG2(level, info, ...)                                                      \
+    G_STMT_START                                                                       \
+    {                                                                                  \
+        if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) {                            \
+            ResolveAddrInfo *_info = (info);                                           \
+            char             _addr_str[NM_UTILS_INET_ADDRSTRLEN];                      \
+                                                                                       \
+            _nm_log((level),                                                           \
+                    (_NMLOG_DOMAIN),                                                   \
+                    0,                                                                 \
+                    NULL,                                                              \
+                    NULL,                                                              \
+                    _NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT                   \
+                                       ",%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),    \
+                    NM_HASH_OBFUSCATE_PTR(_info),                                      \
+                    nm_utils_inet_ntop(_info->addr_family, &_info->address, _addr_str) \
+                        _NM_UTILS_MACRO_REST(__VA_ARGS__));                            \
+        }                                                                              \
+    }                                                                                  \
+    G_STMT_END
+
+static void
+resolve_addr_info_free(ResolveAddrInfo *info)
+{
+    nm_assert(info->cancellable_id == 0);
+    nm_assert(!info->resolved_handle);
+    g_object_unref(info->task);
+    g_free(info);
+}
+
+static void
+resolve_addr_complete(ResolveAddrInfo *info, char *hostname_take, GError *error_take)
+{
+    nm_assert(!!hostname_take != !!error_take);
+
+    nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
+    if (error_take)
+        g_task_return_error(info->task, error_take);
+    else
+        g_task_return_pointer(info->task, hostname_take, g_free);
+
+    resolve_addr_info_free(info);
+}
+
+static void
+resolve_addr_helper_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    ResolveAddrInfo *info        = user_data;
+    gs_free_error GError *error  = NULL;
+    gs_free char *        output = NULL;
+
+    output = nm_utils_spawn_helper_finish(result, &error);
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    _LOG2D(info, "helper returned hostname '%s'", output);
+
+    resolve_addr_complete(info, g_steal_pointer(&output), g_steal_pointer(&error));
+}
+
+static void
+resolve_addr_spawn_helper(ResolveAddrInfo *info)
+{
+    char addr_str[NM_UTILS_INET_ADDRSTRLEN];
+
+    nm_utils_inet_ntop(info->addr_family, &info->address, addr_str);
+    _LOG2D(info, "start lookup via nm-daemon-helper");
+    nm_utils_spawn_helper(NM_MAKE_STRV("resolve-address", addr_str),
+                          g_task_get_cancellable(info->task),
+                          resolve_addr_helper_cb,
+                          info);
+}
+
+static void
+resolve_addr_resolved_cb(NMDnsSystemdResolved *                   resolved,
+                         NMDnsSystemdResolvedResolveHandle *      handle,
+                         const NMDnsSystemdResolvedAddressResult *names,
+                         guint                                    names_len,
+                         guint64                                  flags,
+                         GError *                                 error,
+                         gpointer                                 user_data)
+{
+    ResolveAddrInfo *info = user_data;
+
+    info->resolved_handle = NULL;
+
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    if (error) {
+        gs_free char *dbus_error = NULL;
+
+        _LOG2D(info, "error resolving via systemd-resolved: %s", error->message);
+
+        dbus_error = g_dbus_error_get_remote_error(error);
+        if (nm_streq0(dbus_error, "org.freedesktop.resolve1.DnsError.NXDOMAIN")) {
+            resolve_addr_complete(info, NULL, g_error_copy(error));
+            return;
+        }
+
+        resolve_addr_spawn_helper(info);
+        return;
+    }
+
+    if (names_len == 0) {
+        _LOG2D(info, "systemd-resolved returned no result");
+        resolve_addr_complete(info, g_strdup(""), NULL);
+        return;
+    }
+
+    _LOG2D(info, "systemd-resolved returned hostname '%s'", names[0].name);
+    resolve_addr_complete(info, g_strdup(names[0].name), NULL);
+}
+
+static void
+resolve_addr_cancelled(GObject *object, gpointer user_data)
+{
+    ResolveAddrInfo *info  = user_data;
+    GError *         error = NULL;
+
+    nm_clear_g_signal_handler(g_task_get_cancellable(info->task), &info->cancellable_id);
+    nm_clear_pointer(&info->resolved_handle, nm_dns_systemd_resolved_resolve_cancel);
+    nm_utils_error_set_cancelled(&error, FALSE, NULL);
+    resolve_addr_complete(info, NULL, error);
+}
+
+void
+nm_device_resolve_address(int                 addr_family,
+                          gconstpointer       address,
+                          GCancellable *      cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer            cb_data)
+{
+    ResolveAddrInfo *     info;
+    NMDnsSystemdResolved *resolved;
+
+    info  = g_new(ResolveAddrInfo, 1);
+    *info = (ResolveAddrInfo){
+        .task = nm_g_task_new(NULL, cancellable, nm_device_resolve_address, callback, cb_data),
+        .addr_family = addr_family,
+        .address     = nm_ip_addr_init(addr_family, address),
+    };
+
+    if (cancellable) {
+        gulong signal_id;
+
+        signal_id =
+            g_cancellable_connect(cancellable, G_CALLBACK(resolve_addr_cancelled), info, NULL);
+        if (signal_id == 0) {
+            /* the request is already cancelled. Return. */
+            return;
+        }
+        info->cancellable_id = signal_id;
+    }
+
+    resolved = (NMDnsSystemdResolved *) nm_dns_manager_get_systemd_resolved(nm_dns_manager_get());
+    if (resolved) {
+        _LOG2D(info, "start lookup via systemd-resolved");
+        info->resolved_handle =
+            nm_dns_systemd_resolved_resolve_address(resolved,
+                                                    0,
+                                                    addr_family,
+                                                    address,
+                                                    SD_RESOLVED_DNS | SD_RESOLVED_NO_SYNTHESIZE,
+                                                    20000,
+                                                    resolve_addr_resolved_cb,
+                                                    info);
+        return;
+    }
+
+    resolve_addr_spawn_helper(info);
+}
+
+char *
+nm_device_resolve_address_finish(GAsyncResult *result, GError **error)
+{
+    GTask *task = G_TASK(result);
+
+    nm_assert(nm_g_task_is_valid(result, NULL, nm_device_resolve_address));
+
+    return g_task_propagate_pointer(task, error);
+}
diff --git a/src/core/devices/nm-device-utils.h b/src/core/devices/nm-device-utils.h
new file mode 100644
index 00000000..8bc957a4
--- /dev/null
+++ b/src/core/devices/nm-device-utils.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __DEVICES_NM_DEVICE_UTILS_H__
+#define __DEVICES_NM_DEVICE_UTILS_H__
+
+/*****************************************************************************/
+
+const char *nm_device_state_to_str(NMDeviceState state);
+const char *nm_device_state_reason_to_str(NMDeviceStateReason reason);
+
+#define nm_device_state_reason_to_str_a(reason) \
+    NM_UTILS_LOOKUP_STR_A(nm_device_state_reason_to_str, reason)
+
+static inline NMDeviceStateReason
+nm_device_state_reason_check(NMDeviceStateReason reason)
+{
+    /* the device-state-reason serves mostly informational purpose during a state
+     * change. In some cases however, decisions are made based on the reason.
+     * I tend to think that interpreting the state reason to derive some behaviors
+     * is confusing, because the cause and effect are so far apart.
+     *
+     * This function is here to mark source that inspects the reason to make
+     * a decision -- contrary to places that set the reason. Thus, by grepping
+     * for nm_device_state_reason_check() you can find the "effect" to a certain
+     * reason.
+     */
+    return reason;
+}
+
+/*****************************************************************************/
+
+#define NM_PENDING_ACTION_AUTOACTIVATE           "autoactivate"
+#define NM_PENDING_ACTION_IN_STATE_CHANGE        "in-state-change"
+#define NM_PENDING_ACTION_RECHECK_AVAILABLE      "recheck-available"
+#define NM_PENDING_ACTION_CARRIER_WAIT           "carrier-wait"
+#define NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT "waiting-for-supplicant"
+#define NM_PENDING_ACTION_WIFI_SCAN              "wifi-scan"
+#define NM_PENDING_ACTION_WAITING_FOR_COMPANION  "waiting-for-companion"
+#define NM_PENDING_ACTION_LINK_INIT              "link-init"
+
+#define NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "queued-state-change-"
+#define NM_PENDING_ACTIONPREFIX_ACTIVATION          "activation-"
+
+const char *nm_device_state_queued_state_to_str(NMDeviceState state);
+
+/*****************************************************************************/
+
+typedef enum {
+    NM_DEVICE_MTU_SOURCE_NONE,
+    NM_DEVICE_MTU_SOURCE_PARENT,
+    NM_DEVICE_MTU_SOURCE_IP_CONFIG,
+    NM_DEVICE_MTU_SOURCE_CONNECTION,
+} NMDeviceMtuSource;
+
+const char *nm_device_mtu_source_to_str(NMDeviceMtuSource mtu_source);
+
+/*****************************************************************************/
+
+typedef enum _nm_packed {
+    NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+    NM_DEVICE_SYS_IFACE_STATE_ASSUME,
+    NM_DEVICE_SYS_IFACE_STATE_MANAGED,
+
+    /* the REMOVED state applies when the device is manually set to unmanaged
+     * or the link was externally removed. In both cases, we move the device
+     * to UNMANAGED state, without touching the link -- be it, because the link
+     * is already gone or because we want to release it (give it up).
+     */
+    NM_DEVICE_SYS_IFACE_STATE_REMOVED,
+} NMDeviceSysIfaceState;
+
+const char *nm_device_sys_iface_state_to_str(NMDeviceSysIfaceState sys_iface_state);
+
+/*****************************************************************************/
+
+typedef enum {
+    NM_DEVICE_IP_STATE_NONE,
+    NM_DEVICE_IP_STATE_WAIT,
+    NM_DEVICE_IP_STATE_CONF,
+    NM_DEVICE_IP_STATE_DONE,
+    NM_DEVICE_IP_STATE_FAIL,
+} NMDeviceIPState;
+
+const char *nm_device_ip_state_to_str(NMDeviceIPState ip_state);
+
+/*****************************************************************************/
+
+/*****************************************************************************/
+
+void nm_device_resolve_address(int                 addr_family,
+                               gconstpointer       address,
+                               GCancellable *      cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer            cb_data);
+
+char *nm_device_resolve_address_finish(GAsyncResult *result, GError **error);
+
+#endif /* __DEVICES_NM_DEVICE_UTILS_H__ */
diff --git a/src/core/devices/nm-device-veth.c b/src/core/devices/nm-device-veth.c
index e0ba843d..28831b75 100644
--- a/src/core/devices/nm-device-veth.c
+++ b/src/core/devices/nm-device-veth.c
@@ -7,11 +7,11 @@
 
 #include <stdlib.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-device-veth.h"
 #include "nm-device-private.h"
 #include "nm-manager.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-veth.h"
 
@@ -161,12 +161,8 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 static const NMDBusInterfaceInfoExtended interface_info_device_veth = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_VETH,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Peer",
-                                                             "o",
-                                                             NM_DEVICE_VETH_PEER), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Peer", "o", NM_DEVICE_VETH_PEER), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-vlan.c b/src/core/devices/nm-device-vlan.c
index bfde60ef..548245f8 100644
--- a/src/core/devices/nm-device-vlan.c
+++ b/src/core/devices/nm-device-vlan.c
@@ -16,11 +16,12 @@
 #include "settings/nm-settings.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-manager.h"
-#include "nm-core-internal.h"
-#include "platform/nmp-object.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nm-platform-utils.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceVlan
 #include "nm-device-logging.h"
@@ -436,7 +437,7 @@ update_connection(NMDevice *device, NMConnection *connection)
     const NMPlatformLink *plink;
     const NMPObject *     polnk;
     guint                 vlan_id;
-    guint                 vlan_flags;
+    _NMVlanFlags          vlan_flags;
 
     if (!s_vlan) {
         s_vlan = (NMSettingVlan *) nm_setting_vlan_new();
@@ -463,9 +464,9 @@ update_connection(NMDevice *device, NMConnection *connection)
     if (polnk)
         vlan_flags = polnk->lnk_vlan.flags;
     else
-        vlan_flags = NM_VLAN_FLAG_REORDER_HEADERS;
-    if (vlan_flags != nm_setting_vlan_get_flags(s_vlan))
-        g_object_set(s_vlan, NM_SETTING_VLAN_FLAGS, (NMVlanFlags) vlan_flags, NULL);
+        vlan_flags = _NM_VLAN_FLAG_REORDER_HEADERS;
+    if (NM_VLAN_FLAGS_CAST(vlan_flags) != nm_setting_vlan_get_flags(s_vlan))
+        g_object_set(s_vlan, NM_SETTING_VLAN_FLAGS, NM_VLAN_FLAGS_CAST(vlan_flags), NULL);
 
     if (polnk) {
         _nm_setting_vlan_set_priorities(s_vlan,
@@ -507,7 +508,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
         nm_platform_link_vlan_change(nm_device_get_platform(device),
                                      nm_device_get_ifindex(device),
-                                     NM_VLAN_FLAGS_ALL,
+                                     _NM_VLAN_FLAGS_ALL,
                                      nm_setting_vlan_get_flags(s_vlan),
                                      TRUE,
                                      ingress_map,
@@ -546,17 +547,11 @@ nm_device_vlan_init(NMDeviceVlan *self)
 static const NMDBusInterfaceInfoExtended interface_info_device_vlan = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_VLAN,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Carrier", "b", NM_DEVICE_CARRIER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Parent", "o", NM_DEVICE_PARENT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("VlanId",
-                                                             "u",
-                                                             NM_DEVICE_VLAN_ID), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Carrier", "b", NM_DEVICE_CARRIER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Parent", "o", NM_DEVICE_PARENT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("VlanId", "u", NM_DEVICE_VLAN_ID), ), ),
 };
 
 static void
@@ -674,7 +669,7 @@ get_connection_iface(NMDeviceFactory *factory, NMConnection *connection, const c
      * device, we create one for it using the VLAN ID and the parent
      * interface's name.
      */
-    return nm_utils_new_vlan_name(parent_iface, nm_setting_vlan_get_id(s_vlan));
+    return nmp_utils_new_vlan_name(parent_iface, nm_setting_vlan_get_id(s_vlan));
 }
 
 NM_DEVICE_FACTORY_DEFINE_INTERNAL(
diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c
index 4fec59ba..2a1f42c6 100644
--- a/src/core/devices/nm-device-vrf.c
+++ b/src/core/devices/nm-device-vrf.c
@@ -4,12 +4,12 @@
 
 #include "nm-device-vrf.h"
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-device-factory.h"
 #include "nm-device-private.h"
 #include "nm-manager.h"
 #include "nm-setting-vrf.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "settings/nm-settings.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceVrf
diff --git a/src/core/devices/nm-device-vxlan.c b/src/core/devices/nm-device-vxlan.c
index f16a52c4..f9dfad2a 100644
--- a/src/core/devices/nm-device-vxlan.c
+++ b/src/core/devices/nm-device-vxlan.c
@@ -9,7 +9,7 @@
 
 #include "nm-device-private.h"
 #include "nm-manager.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
 #include "nm-device-factory.h"
 #include "nm-setting-vxlan.h"
@@ -17,7 +17,7 @@
 #include "settings/nm-settings.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceVxlan
 #include "nm-device-logging.h"
@@ -576,38 +576,34 @@ nm_device_vxlan_init(NMDeviceVxlan *self)
 static const NMDBusInterfaceInfoExtended interface_info_device_vxlan = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_VXLAN,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Parent", "o", NM_DEVICE_PARENT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Id", "u", NM_DEVICE_VXLAN_ID),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Group", "s", NM_DEVICE_VXLAN_GROUP),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Local", "s", NM_DEVICE_VXLAN_LOCAL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Tos", "y", NM_DEVICE_VXLAN_TOS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ttl", "y", NM_DEVICE_VXLAN_TTL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Learning",
-                                                             "b",
-                                                             NM_DEVICE_VXLAN_LEARNING),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ageing", "u", NM_DEVICE_VXLAN_AGEING),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Limit", "u", NM_DEVICE_VXLAN_LIMIT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("DstPort",
-                                                             "q",
-                                                             NM_DEVICE_VXLAN_DST_PORT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("SrcPortMin",
-                                                             "q",
-                                                             NM_DEVICE_VXLAN_SRC_PORT_MIN),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("SrcPortMax",
-                                                             "q",
-                                                             NM_DEVICE_VXLAN_SRC_PORT_MAX),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Proxy", "b", NM_DEVICE_VXLAN_PROXY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Rsc", "b", NM_DEVICE_VXLAN_RSC),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("L2miss", "b", NM_DEVICE_VXLAN_L2MISS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("L3miss",
-                                                             "b",
-                                                             NM_DEVICE_VXLAN_L3MISS), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Parent", "o", NM_DEVICE_PARENT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Id", "u", NM_DEVICE_VXLAN_ID),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Group", "s", NM_DEVICE_VXLAN_GROUP),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Local", "s", NM_DEVICE_VXLAN_LOCAL),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Tos", "y", NM_DEVICE_VXLAN_TOS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ttl", "y", NM_DEVICE_VXLAN_TTL),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Learning",
+                                                           "b",
+                                                           NM_DEVICE_VXLAN_LEARNING),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ageing", "u", NM_DEVICE_VXLAN_AGEING),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Limit", "u", NM_DEVICE_VXLAN_LIMIT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DstPort",
+                                                           "q",
+                                                           NM_DEVICE_VXLAN_DST_PORT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("SrcPortMin",
+                                                           "q",
+                                                           NM_DEVICE_VXLAN_SRC_PORT_MIN),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("SrcPortMax",
+                                                           "q",
+                                                           NM_DEVICE_VXLAN_SRC_PORT_MAX),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Proxy", "b", NM_DEVICE_VXLAN_PROXY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Rsc", "b", NM_DEVICE_VXLAN_RSC),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("L2miss", "b", NM_DEVICE_VXLAN_L2MISS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("L3miss",
+                                                           "b",
+                                                           NM_DEVICE_VXLAN_L3MISS), ), ),
 };
 
 static void
diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c
index 5bee09e6..aa9c46eb 100644
--- a/src/core/devices/nm-device-wireguard.c
+++ b/src/core/devices/nm-device-wireguard.c
@@ -11,12 +11,12 @@
 #include <linux/fib_rules.h>
 
 #include "nm-setting-wireguard.h"
-#include "nm-core-internal.h"
-#include "nm-glib-aux/nm-secret-utils.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-glib-aux/nm-secret-utils.h"
 #include "nm-device-private.h"
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
-#include "platform/nmp-rules-manager.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nmp-rules-manager.h"
 #include "nm-device-factory.h"
 #include "nm-active-connection.h"
 #include "nm-act-request.h"
@@ -66,7 +66,7 @@ G_STATIC_ASSERT(NM_WIREGUARD_SYMMETRIC_KEY_LEN == NMP_WIREGUARD_SYMMETRIC_KEY_LE
  * timestamp. */
 #define RETRY_IN_MSEC_ASAP ((gint64) G_MAXINT64)
 
-#define RETRY_IN_MSEC_MAX ((gint64)(30 * 60 * 1000))
+#define RETRY_IN_MSEC_MAX ((gint64) (30 * 60 * 1000))
 
 typedef enum {
     LINK_CONFIG_MODE_FULL,
@@ -669,7 +669,7 @@ _peers_resolve_retry_reschedule(NMDeviceWireGuard *self, gint64 new_next_try_at_
      * than expected. Also, rate-limit to 500 msec. */
     interval_ms = NM_CLAMP((new_next_try_at_nsec - now) / NM_UTILS_NSEC_PER_MSEC,
                            (gint64) 500,
-                           (gint64)(24 * 60 * 60 * 1000));
+                           (gint64) (24 * 60 * 60 * 1000));
 
     _LOGT(LOGD_DEVICE,
           "wireguard-peers: schedule rechecking peer endpoints in %u msec",
@@ -1939,11 +1939,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 
     switch (prop_id) {
     case PROP_PUBLIC_KEY:
-        g_value_take_variant(value,
-                             g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
-                                                       priv->lnk_curr.public_key,
-                                                       sizeof(priv->lnk_curr.public_key),
-                                                       1));
+        g_value_take_variant(
+            value,
+            nm_g_variant_new_ay(priv->lnk_curr.public_key, sizeof(priv->lnk_curr.public_key)));
         break;
     case PROP_LISTEN_PORT:
         g_value_set_uint(value, priv->lnk_curr.listen_port);
diff --git a/src/core/devices/nm-device-wpan.c b/src/core/devices/nm-device-wpan.c
index 2f3b16ff..73d79410 100644
--- a/src/core/devices/nm-device-wpan.c
+++ b/src/core/devices/nm-device-wpan.c
@@ -15,10 +15,10 @@
 #include "nm-act-request.h"
 #include "nm-device-private.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-wpan.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceWpan
 #include "nm-device-logging.h"
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 040dd0b4..1c70ecb3 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -23,22 +23,23 @@
 #include <linux/if_ether.h>
 #include <linux/if_infiniband.h>
 
-#include "nm-std-aux/unaligned.h"
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-glib-aux/nm-random-utils.h"
-#include "systemd/nm-sd-utils-shared.h"
-
-#include "nm-base/nm-ethtool-base.h"
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-std-aux/unaligned.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-random-utils.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
+
+#include "libnm-base/nm-ethtool-base.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-device-private.h"
 #include "nm-l3cfg.h"
 #include "nm-l3-config-data.h"
 #include "NetworkManagerUtils.h"
 #include "nm-manager.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "platform/nmp-object.h"
-#include "platform/nmp-rules-manager.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nmp-rules-manager.h"
 #include "ndisc/nm-ndisc.h"
 #include "ndisc/nm-lndp-ndisc.h"
 #include "dhcp/nm-dhcp-manager.h"
@@ -51,7 +52,8 @@
 #include "dnsmasq/nm-dnsmasq-manager.h"
 #include "nm-dhcp-config.h"
 #include "nm-rfkill-manager.h"
-#include "nm-firewall-manager.h"
+#include "nm-firewall-utils.h"
+#include "nm-firewalld-manager.h"
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
 #include "nm-setting-ethtool.h"
@@ -65,12 +67,13 @@
 #include "c-list/src/c-list.h"
 #include "dns/nm-dns-manager.h"
 #include "nm-acd-manager.h"
-#include "nm-core-internal.h"
-#include "systemd/nm-sd.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-systemd-core/nm-sd.h"
 #include "nm-lldp-listener.h"
 #include "nm-audit-manager.h"
 #include "nm-connectivity.h"
 #include "nm-dbus-interface.h"
+#include "nm-hostname-manager.h"
 
 #include "nm-device-generic.h"
 #include "nm-device-vlan.h"
@@ -125,7 +128,6 @@ typedef struct {
 typedef struct {
     NMDevice *device;
     guint     idle_add_id;
-    int       ifindex;
 } DeleteOnDeactivateData;
 
 typedef struct {
@@ -208,6 +210,7 @@ typedef struct {
     NMOptionBool            requested[_NM_ETHTOOL_ID_FEATURE_NUM];
     NMEthtoolCoalesceState *coalesce;
     NMEthtoolRingState *    ring;
+    NMEthtoolPauseState *   pause;
 } EthtoolState;
 
 typedef enum {
@@ -218,7 +221,6 @@ typedef enum {
 
 typedef struct {
     ResolverState state;
-    GResolver *   resolver;
     GInetAddress *address;
     GCancellable *cancellable;
     char *        hostname;
@@ -497,9 +499,6 @@ typedef struct _NMDevicePrivate {
 
     NMDeviceStageState stage1_sriov_state : 3;
 
-    /* Generic DHCP stuff */
-    char *dhcp_anycast_address;
-
     char *current_stable_id;
 
     /* Proxy Configuration */
@@ -593,9 +592,9 @@ typedef struct _NMDevicePrivate {
     gulong            dnsmasq_state_id;
 
     /* Firewall */
-    FirewallState            fw_state : 4;
-    NMFirewallManager *      fw_mgr;
-    NMFirewallManagerCallId *fw_call;
+    FirewallState             fw_state : 4;
+    NMFirewalldManager *      fw_mgr;
+    NMFirewalldManagerCallId *fw_call;
 
     /* IPv4LL stuff */
     sd_ipv4ll *ipv4ll;
@@ -694,6 +693,8 @@ typedef struct _NMDevicePrivate {
     } stats;
 
     bool mtu_force_set_done : 1;
+
+    NMOptionBool promisc_reset;
 } NMDevicePrivate;
 
 G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT)
@@ -768,139 +769,6 @@ static gboolean device_link_changed(NMDevice *self);
 
 /*****************************************************************************/
 
-static NM_UTILS_LOOKUP_STR_DEFINE(
-    queued_state_to_string,
-    NMDeviceState,
-    NM_UTILS_LOOKUP_DEFAULT(NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "???"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_UNKNOWN,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "unknown"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_UNMANAGED,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "unmanaged"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_UNAVAILABLE,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "unavailable"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_DISCONNECTED,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "disconnected"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_PREPARE,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "prepare"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_CONFIG,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "config"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_NEED_AUTH,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "need-auth"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_IP_CONFIG,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "ip-config"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_IP_CHECK,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "ip-check"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_SECONDARIES,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "secondaries"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_ACTIVATED,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "activated"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_DEACTIVATING,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "deactivating"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_FAILED,
-                             NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "failed"), );
-
-const char *
-nm_device_state_to_str(NMDeviceState state)
-{
-    return queued_state_to_string(state) + NM_STRLEN(NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE);
-}
-
-NM_UTILS_LOOKUP_STR_DEFINE(
-    nm_device_state_reason_to_str,
-    NMDeviceStateReason,
-    NM_UTILS_LOOKUP_DEFAULT(NULL),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNKNOWN, "unknown"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NONE, "none"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NOW_MANAGED, "managed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NOW_UNMANAGED, "unmanaged"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CONFIG_FAILED, "config-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE, "ip-config-unavailable"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED, "ip-config-expired"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NO_SECRETS, "no-secrets"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT, "supplicant-disconnect"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED,
-                             "supplicant-config-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED, "supplicant-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT, "supplicant-timeout"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PPP_START_FAILED, "ppp-start-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PPP_DISCONNECT, "ppp-disconnect"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PPP_FAILED, "ppp-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DHCP_START_FAILED, "dhcp-start-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DHCP_ERROR, "dhcp-error"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DHCP_FAILED, "dhcp-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SHARED_START_FAILED, "sharing-start-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SHARED_FAILED, "sharing-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED, "autoip-start-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_ERROR, "autoip-error"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_FAILED, "autoip-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_BUSY, "modem-busy"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE, "modem-no-dialtone"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER, "modem-no-carrier"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT, "modem-dial-timeout"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED, "modem-dial-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED, "modem-init-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_APN_FAILED, "gsm-apn-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING,
-                             "gsm-registration-idle"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED,
-                             "gsm-registration-denied"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT,
-                             "gsm-registration-timeout"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED,
-                             "gsm-registration-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED, "gsm-pin-check-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_FIRMWARE_MISSING, "firmware-missing"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_REMOVED, "removed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SLEEPING, "sleeping"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CONNECTION_REMOVED, "connection-removed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_USER_REQUESTED, "user-requested"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CARRIER, "carrier-changed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED, "connection-assumed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, "supplicant-available"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND, "modem-not-found"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_BT_FAILED, "bluetooth-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED, "gsm-sim-not-inserted"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED, "gsm-sim-pin-required"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED, "gsm-sim-puk-required"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_WRONG, "gsm-sim-wrong"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_INFINIBAND_MODE, "infiniband-mode"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED, "dependency-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_BR2684_FAILED, "br2684-bridge-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE,
-                             "modem-manager-unavailable"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SSID_NOT_FOUND, "ssid-not-found"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED,
-                             "secondary-connection-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED, "dcb-fcoe-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED, "teamd-control-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_FAILED, "modem-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_AVAILABLE, "modem-available"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT, "sim-pin-incorrect"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NEW_ACTIVATION, "new-activation"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PARENT_CHANGED, "parent-changed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED,
-                             "parent-managed-changed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE, "ip-address-duplicate"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, "ip-method-unsupported"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED,
-                             "sriov-configuration-failed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, "peer-not-found"), );
-
-#define reason_to_string_a(reason) NM_UTILS_LOOKUP_STR_A(nm_device_state_reason_to_str, reason)
-
-static NM_UTILS_LOOKUP_STR_DEFINE(mtu_source_to_str,
-                                  NMDeviceMtuSource,
-                                  NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_NONE, "none"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_PARENT, "parent"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_IP_CONFIG,
-                                                           "ip-config"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_CONNECTION,
-                                                           "connection"), );
-
-/*****************************************************************************/
-
 static void
 _hostname_resolver_free(HostnameResolver *resolver)
 {
@@ -909,7 +777,6 @@ _hostname_resolver_free(HostnameResolver *resolver)
 
     nm_clear_g_source(&resolver->timeout_id);
     nm_clear_g_cancellable(&resolver->cancellable);
-    nm_g_object_unref(resolver->resolver);
     nm_g_object_unref(resolver->address);
     g_free(resolver->hostname);
     nm_g_slice_free(resolver);
@@ -953,8 +820,7 @@ _prop_get_connection_stable_id(NMDevice *         self,
      * Especially with ${RANDOM} stable-id we want to generate *one* configuration
      * for each activation. */
     if (G_UNLIKELY(!priv->current_stable_id)) {
-        gs_free char *       default_id = NULL;
-        gs_free char *       generated  = NULL;
+        gs_free char *       generated = NULL;
         NMUtilsStableType    stable_type;
         NMSettingConnection *s_con;
         gboolean             hwaddr_is_fake;
@@ -967,11 +833,10 @@ _prop_get_connection_stable_id(NMDevice *         self,
         stable_id = nm_setting_connection_get_stable_id(s_con);
 
         if (!stable_id) {
-            default_id =
+            stable_id =
                 nm_config_data_get_connection_default(NM_CONFIG_GET_DATA,
                                                       NM_CON_DEFAULT("connection.stable-id"),
                                                       self);
-            stable_id = default_id;
         }
 
         uuid = nm_connection_get_uuid(connection);
@@ -1030,7 +895,6 @@ _prop_get_ipv6_dhcp_duid(NMDevice *    self,
 {
     NMSettingIPConfig *s_ip6;
     const char *       duid;
-    gs_free char *     duid_default = NULL;
     const char *       duid_error;
     GBytes *           duid_out;
     gboolean           duid_enforce = TRUE;
@@ -1043,10 +907,9 @@ _prop_get_ipv6_dhcp_duid(NMDevice *    self,
     duid  = nm_setting_ip6_config_get_dhcp_duid(NM_SETTING_IP6_CONFIG(s_ip6));
 
     if (!duid) {
-        duid_default = nm_config_data_get_connection_default(NM_CONFIG_GET_DATA,
-                                                             NM_CON_DEFAULT("ipv6.dhcp-duid"),
-                                                             self);
-        duid         = duid_default;
+        duid = nm_config_data_get_connection_default(NM_CONFIG_GET_DATA,
+                                                     NM_CON_DEFAULT("ipv6.dhcp-duid"),
+                                                     self);
         if (!duid)
             duid = "lease";
     }
@@ -1397,7 +1260,7 @@ _prop_get_ipvx_route_table(NMDevice *self, int addr_family)
     _LOGT(LOGD_DEVICE,
           "ipv%c.route-table = %u%s",
           nm_utils_addr_family_to_char(addr_family),
-          (guint)(route_table ?: RT_TABLE_MAIN),
+          (guint) (route_table ?: RT_TABLE_MAIN),
           route_table != 0u ? "" : " (policy routing not enabled)");
 
     return route_table;
@@ -1523,7 +1386,6 @@ _prop_get_ipvx_dhcp_iaid(NMDevice *    self,
     const int          IS_IPv4 = NM_IS_IPv4(addr_family);
     NMSettingIPConfig *s_ip;
     const char *       iaid_str;
-    gs_free char *     iaid_str_free = NULL;
     guint32            iaid;
     const char *       iface;
     const char *       fail_reason;
@@ -1532,11 +1394,10 @@ _prop_get_ipvx_dhcp_iaid(NMDevice *    self,
     s_ip     = nm_connection_get_setting_ip_config(connection, addr_family);
     iaid_str = nm_setting_ip_config_get_dhcp_iaid(s_ip);
     if (!iaid_str) {
-        iaid_str_free = nm_config_data_get_connection_default(
-            NM_CONFIG_GET_DATA,
-            IS_IPv4 ? NM_CON_DEFAULT("ipv4.dhcp-iaid") : NM_CON_DEFAULT("ipv6.dhcp-iaid"),
-            self);
-        iaid_str = iaid_str_free;
+        iaid_str = nm_config_data_get_connection_default(NM_CONFIG_GET_DATA,
+                                                         IS_IPv4 ? NM_CON_DEFAULT("ipv4.dhcp-iaid")
+                                                                 : NM_CON_DEFAULT("ipv6.dhcp-iaid"),
+                                                         self);
         if (!iaid_str) {
             iaid_str    = NM_IAID_IFNAME;
             is_explicit = FALSE;
@@ -1567,7 +1428,7 @@ _prop_get_ipvx_dhcp_iaid(NMDevice *    self,
         iaid = unaligned_read_be32(&pllink->l_address.data[pllink->l_address.len - 4]);
         goto out_good;
     } else if (nm_streq0(iaid_str, NM_IAID_PERM_MAC)) {
-        guint8      hwaddr_buf[NM_UTILS_HWADDR_LEN_MAX];
+        guint8      hwaddr_buf[_NM_UTILS_HWADDR_LEN_MAX];
         const char *hwaddr_str;
         gsize       hwaddr_len;
 
@@ -1693,12 +1554,10 @@ _prop_get_ipvx_dhcp_hostname_flags(NMDevice *self, int addr_family)
 }
 
 static const char *
-_prop_get_connection_mud_url(NMDevice *self, NMSettingConnection *s_con, char **out_mud_url)
+_prop_get_connection_mud_url(NMDevice *self, NMSettingConnection *s_con)
 {
-    const char *  mud_url;
-    gs_free char *s = NULL;
-
-    nm_assert(out_mud_url && !*out_mud_url);
+    const char *mud_url;
+    const char *s;
 
     mud_url = nm_setting_connection_get_mud_url(s_con);
 
@@ -1715,7 +1574,7 @@ _prop_get_connection_mud_url(NMDevice *self, NMSettingConnection *s_con, char **
         if (nm_streq(s, NM_CONNECTION_MUD_URL_NONE))
             return NULL;
         if (nm_sd_http_url_is_valid_https(s))
-            return (*out_mud_url = g_steal_pointer(&s));
+            return s;
     }
 
     return NULL;
@@ -1726,10 +1585,9 @@ _prop_get_ipv4_dhcp_client_id(NMDevice *self, NMConnection *connection, GBytes *
 {
     NMSettingIPConfig *s_ip4;
     const char *       client_id;
-    gs_free char *     client_id_default = NULL;
     guint8 *           client_id_buf;
     const char *       fail_reason;
-    guint8             hwaddr_bin_buf[NM_UTILS_HWADDR_LEN_MAX];
+    guint8             hwaddr_bin_buf[_NM_UTILS_HWADDR_LEN_MAX];
     const guint8 *     hwaddr_bin;
     int                arp_type;
     gsize              hwaddr_len;
@@ -1740,13 +1598,12 @@ _prop_get_ipv4_dhcp_client_id(NMDevice *self, NMConnection *connection, GBytes *
     client_id = nm_setting_ip4_config_get_dhcp_client_id(NM_SETTING_IP4_CONFIG(s_ip4));
 
     if (!client_id) {
-        client_id_default =
-            nm_config_data_get_connection_default(NM_CONFIG_GET_DATA,
-                                                  NM_CON_DEFAULT("ipv4.dhcp-client-id"),
-                                                  self);
-        if (client_id_default && client_id_default[0]) {
+        client_id = nm_config_data_get_connection_default(NM_CONFIG_GET_DATA,
+                                                          NM_CON_DEFAULT("ipv4.dhcp-client-id"),
+                                                          self);
+        if (client_id && !client_id[0]) {
             /* a non-empty client-id is always valid, see nm_dhcp_utils_client_id_string_to_bytes().  */
-            client_id = client_id_default;
+            client_id = NULL;
         }
     }
 
@@ -1878,8 +1735,7 @@ out_good:
 static GBytes *
 _prop_get_ipv4_dhcp_vendor_class_identifier(NMDevice *self, NMSettingIP4Config *s_ip4)
 {
-    gs_free char *config_data_prop = NULL;
-    gs_free char *to_free          = NULL;
+    gs_free char *to_free = NULL;
     const char *  conn_prop;
     GBytes *      bytes = NULL;
     const char *  bin;
@@ -1889,13 +1745,13 @@ _prop_get_ipv4_dhcp_vendor_class_identifier(NMDevice *self, NMSettingIP4Config *
 
     if (!conn_prop) {
         /* set in NetworkManager.conf ? */
-        config_data_prop = nm_config_data_get_connection_default(
+        conn_prop = nm_config_data_get_connection_default(
             NM_CONFIG_GET_DATA,
             NM_CON_DEFAULT("ipv4.dhcp-vendor-class-identifier"),
             self);
 
-        if (config_data_prop && nm_utils_validate_dhcp4_vendor_class_id(config_data_prop, NULL))
-            conn_prop = config_data_prop;
+        if (conn_prop && !nm_utils_validate_dhcp4_vendor_class_id(conn_prop, NULL))
+            conn_prop = NULL;
     }
 
     if (conn_prop) {
@@ -1961,16 +1817,11 @@ _prop_get_ipv6_ip6_privacy(NMDevice *self)
 }
 
 static const char *
-_prop_get_x_cloned_mac_address(NMDevice *    self,
-                               NMConnection *connection,
-                               gboolean      is_wifi,
-                               char **       out_addr)
+_prop_get_x_cloned_mac_address(NMDevice *self, NMConnection *connection, gboolean is_wifi)
 {
     NMSetting * setting;
     const char *addr = NULL;
 
-    nm_assert(out_addr && !*out_addr);
-
     setting = nm_connection_get_setting(connection,
                                         is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED);
     if (setting) {
@@ -1979,7 +1830,7 @@ _prop_get_x_cloned_mac_address(NMDevice *    self,
     }
 
     if (!addr) {
-        gs_free char *a = NULL;
+        const char *a;
 
         a = nm_config_data_get_connection_default(
             NM_CONFIG_GET_DATA,
@@ -1994,36 +1845,28 @@ _prop_get_x_cloned_mac_address(NMDevice *    self,
                 NMSettingMacRandomization v;
 
                 /* for backward compatibility, read the deprecated wifi.mac-address-randomization setting. */
-                a = nm_config_data_get_connection_default(
+                v = nm_config_data_get_connection_default_int64(
                     NM_CONFIG_GET_DATA,
                     NM_CON_DEFAULT("wifi.mac-address-randomization"),
-                    self);
-                v = _nm_utils_ascii_str_to_int64(a,
-                                                 10,
-                                                 NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
-                                                 NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
-                                                 NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
+                    self,
+                    NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
+                    NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
+                    NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
                 if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS)
                     addr = NM_CLONED_MAC_RANDOM;
             }
         } else if (NM_CLONED_MAC_IS_SPECIAL(a) || nm_utils_hwaddr_valid(a, ETH_ALEN))
-            addr = *out_addr = g_steal_pointer(&a);
+            addr = a;
     }
 
     return addr;
 }
 
 static const char *
-_prop_get_x_generate_mac_address_mask(NMDevice *    self,
-                                      NMConnection *connection,
-                                      gboolean      is_wifi,
-                                      char **       out_value)
+_prop_get_x_generate_mac_address_mask(NMDevice *self, NMConnection *connection, gboolean is_wifi)
 {
     NMSetting * setting;
-    const char *value = NULL;
-    char *      a;
-
-    nm_assert(out_value && !*out_value);
+    const char *value;
 
     setting = nm_connection_get_setting(connection,
                                         is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED);
@@ -2036,15 +1879,11 @@ _prop_get_x_generate_mac_address_mask(NMDevice *    self,
             return value;
     }
 
-    a = nm_config_data_get_connection_default(
+    return nm_config_data_get_connection_default(
         NM_CONFIG_GET_DATA,
         is_wifi ? NM_CON_DEFAULT("wifi.generate-mac-address-mask")
                 : NM_CON_DEFAULT("ethernet.generate-mac-address-mask"),
         self);
-    if (!a)
-        return NULL;
-    *out_value = a;
-    return a;
 }
 
 /*****************************************************************************/
@@ -2273,6 +2112,113 @@ _ethtool_ring_set(NMDevice *        self,
 }
 
 static void
+_ethtool_pause_reset(NMDevice *self, NMPlatform *platform, EthtoolState *ethtool_state)
+{
+    gs_free NMEthtoolPauseState *pause = NULL;
+
+    nm_assert(NM_IS_DEVICE(self));
+    nm_assert(NM_IS_PLATFORM(platform));
+    nm_assert(ethtool_state);
+
+    pause = g_steal_pointer(&ethtool_state->pause);
+    if (!pause)
+        return;
+
+    if (!nm_platform_ethtool_set_pause(platform, ethtool_state->ifindex, pause))
+        _LOGW(LOGD_DEVICE, "ethtool: failure resetting one or more pause settings");
+    else
+        _LOGD(LOGD_DEVICE, "ethtool: pause settings successfully reset");
+}
+
+static void
+_ethtool_pause_set(NMDevice *        self,
+                   NMPlatform *      platform,
+                   EthtoolState *    ethtool_state,
+                   NMSettingEthtool *s_ethtool)
+{
+    NMEthtoolPauseState pause_old;
+    NMEthtoolPauseState pause_new;
+    GHashTable *        hash;
+    GHashTableIter      iter;
+    const char *        name;
+    GVariant *          variant;
+    gboolean            has_old       = FALSE;
+    NMTernary           pause_autoneg = NM_TERNARY_DEFAULT;
+    NMTernary           pause_rx      = NM_TERNARY_DEFAULT;
+    NMTernary           pause_tx      = NM_TERNARY_DEFAULT;
+
+    nm_assert(NM_IS_DEVICE(self));
+    nm_assert(NM_IS_PLATFORM(platform));
+    nm_assert(NM_IS_SETTING_ETHTOOL(s_ethtool));
+    nm_assert(ethtool_state);
+    nm_assert(!ethtool_state->pause);
+
+    hash = _nm_setting_option_hash(NM_SETTING(s_ethtool), FALSE);
+    if (!hash)
+        return;
+
+    g_hash_table_iter_init(&iter, hash);
+    while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &variant)) {
+        NMEthtoolID ethtool_id = nm_ethtool_id_get_by_name(name);
+
+        if (!nm_ethtool_id_is_pause(ethtool_id))
+            continue;
+
+        nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN));
+
+        if (!has_old) {
+            if (!nm_platform_ethtool_get_link_pause(platform, ethtool_state->ifindex, &pause_old)) {
+                _LOGW(LOGD_DEVICE,
+                      "ethtool: failure setting pause options (cannot read "
+                      "existing setting)");
+                return;
+            }
+            has_old = TRUE;
+        }
+
+        switch (ethtool_id) {
+        case NM_ETHTOOL_ID_PAUSE_AUTONEG:
+            pause_autoneg = g_variant_get_boolean(variant);
+            break;
+        case NM_ETHTOOL_ID_PAUSE_RX:
+            pause_rx = g_variant_get_boolean(variant);
+            break;
+        case NM_ETHTOOL_ID_PAUSE_TX:
+            pause_tx = g_variant_get_boolean(variant);
+            break;
+        default:
+            nm_assert_not_reached();
+        }
+    }
+
+    if (!has_old)
+        return;
+
+    if (pause_rx != NM_TERNARY_DEFAULT || pause_tx != NM_TERNARY_DEFAULT) {
+        /* this implies to explicitly disable autoneg. */
+        nm_assert(pause_autoneg != NM_TERNARY_TRUE);
+        pause_autoneg = NM_TERNARY_FALSE;
+    }
+
+    pause_new = pause_old;
+    if (pause_autoneg != NM_TERNARY_DEFAULT)
+        pause_new.autoneg = !!pause_autoneg;
+    if (pause_rx != NM_TERNARY_DEFAULT)
+        pause_new.rx = !!pause_rx;
+    if (pause_tx != NM_TERNARY_DEFAULT)
+        pause_new.tx = !!pause_tx;
+
+    ethtool_state->pause = nm_memdup(&pause_old, sizeof(pause_old));
+
+    if (!nm_platform_ethtool_set_pause(platform, ethtool_state->ifindex, &pause_new)) {
+        _LOGW(LOGD_DEVICE, "ethtool: failure setting pause settings");
+        return;
+    }
+
+    _LOGD(LOGD_DEVICE, "ethtool: pause settings successfully set");
+}
+
+static void
 _ethtool_state_reset(NMDevice *self)
 {
     NMPlatform *     platform           = nm_device_get_platform(self);
@@ -2288,6 +2234,8 @@ _ethtool_state_reset(NMDevice *self)
         _ethtool_coalesce_reset(self, platform, ethtool_state);
     if (ethtool_state->ring)
         _ethtool_ring_reset(self, platform, ethtool_state);
+    if (ethtool_state->pause)
+        _ethtool_pause_reset(self, platform, ethtool_state);
 }
 
 static void
@@ -2321,8 +2269,10 @@ _ethtool_state_set(NMDevice *self)
     _ethtool_features_set(self, platform, ethtool_state, s_ethtool);
     _ethtool_coalesce_set(self, platform, ethtool_state, s_ethtool);
     _ethtool_ring_set(self, platform, ethtool_state, s_ethtool);
+    _ethtool_pause_set(self, platform, ethtool_state, s_ethtool);
 
-    if (ethtool_state->features || ethtool_state->coalesce || ethtool_state->ring)
+    if (ethtool_state->features || ethtool_state->coalesce || ethtool_state->ring
+        || ethtool_state->pause)
         priv->ethtool_state = g_steal_pointer(&ethtool_state);
 }
 
@@ -2521,15 +2471,6 @@ applied_config_reset_nameservers(AppliedConfig *config)
 
 /*****************************************************************************/
 
-static NM_UTILS_LOOKUP_STR_DEFINE(
-    _sys_iface_state_to_str,
-    NMDeviceSysIfaceState,
-    NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_EXTERNAL, "external"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_ASSUME, "assume"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_MANAGED, "managed"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_REMOVED, "removed"), );
-
 NMDeviceSysIfaceState
 nm_device_sys_iface_state_get(NMDevice *self)
 {
@@ -2568,8 +2509,8 @@ nm_device_sys_iface_state_set(NMDevice *self, NMDeviceSysIfaceState sys_iface_st
     if (priv->sys_iface_state != sys_iface_state) {
         _LOGT(LOGD_DEVICE,
               "sys-iface-state: %s -> %s",
-              _sys_iface_state_to_str(priv->sys_iface_state),
-              _sys_iface_state_to_str(sys_iface_state));
+              nm_device_sys_iface_state_to_str(priv->sys_iface_state),
+              nm_device_sys_iface_state_to_str(sys_iface_state));
         priv->sys_iface_state_ = sys_iface_state;
     }
 
@@ -2600,6 +2541,42 @@ _active_connection_set_state_flags(NMDevice *self, NMActivationStateFlags flags)
 
 /*****************************************************************************/
 
+static gboolean
+set_interface_flags_full(NMDevice *             self,
+                         NMDeviceInterfaceFlags mask,
+                         NMDeviceInterfaceFlags interface_flags,
+                         gboolean               notify)
+{
+    NMDevicePrivate *      priv = NM_DEVICE_GET_PRIVATE(self);
+    NMDeviceInterfaceFlags f;
+
+    nm_assert(!!mask);
+    nm_assert(!NM_FLAGS_ANY(mask, ~_NM_DEVICE_INTERFACE_FLAG_ALL));
+    nm_assert(!NM_FLAGS_ANY(interface_flags, ~mask));
+
+    f = (priv->interface_flags & ~mask) | (interface_flags & mask);
+
+    if (f == priv->interface_flags)
+        return FALSE;
+
+    priv->interface_flags = f;
+    if (notify)
+        _notify(self, PROP_INTERFACE_FLAGS);
+    return TRUE;
+}
+
+static gboolean
+set_interface_flags(NMDevice *             self,
+                    NMDeviceInterfaceFlags interface_flags,
+                    gboolean               set,
+                    gboolean               notify)
+{
+    return set_interface_flags_full(self,
+                                    interface_flags,
+                                    set ? interface_flags : NM_DEVICE_INTERFACE_FLAG_NONE,
+                                    notify);
+}
+
 void
 nm_device_assume_state_get(NMDevice *   self,
                            gboolean *   out_assume_state_guess_assume,
@@ -2810,15 +2787,6 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities)
 
 /*****************************************************************************/
 
-static NM_UTILS_LOOKUP_STR_DEFINE(_ip_state_to_string,
-                                  NMDeviceIPState,
-                                  NM_UTILS_LOOKUP_DEFAULT_WARN("unknown"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_NONE, "none"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_WAIT, "wait"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_CONF, "conf"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_DONE, "done"),
-                                  NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_FAIL, "fail"), );
-
 static void
 _set_ip_state(NMDevice *self, int addr_family, NMDeviceIPState new_state)
 {
@@ -2834,7 +2802,7 @@ _set_ip_state(NMDevice *self, int addr_family, NMDeviceIPState new_state)
           "ip%c-state: set to %d (%s)",
           nm_utils_addr_family_to_char(addr_family),
           (int) new_state,
-          _ip_state_to_string(new_state));
+          nm_device_ip_state_to_str(new_state));
 
     priv->ip_state_x_[IS_IPv4] = new_state;
 
@@ -2943,10 +2911,10 @@ nm_device_take_over_link(NMDevice *self, int ifindex, char **old_name, GError **
 
         /* Rename the link to the device ifname */
         if (up)
-            nm_platform_link_set_down(platform, ifindex);
+            nm_platform_link_change_flags(platform, ifindex, IFF_UP, FALSE);
         success = nm_platform_link_set_name(platform, ifindex, nm_device_get_iface(self));
         if (up)
-            nm_platform_link_set_up(platform, ifindex, NULL);
+            nm_platform_link_change_flags(platform, ifindex, IFF_UP, TRUE);
 
         if (!success) {
             nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "failure renaming link %d", ifindex);
@@ -3074,7 +3042,7 @@ _set_ip_ifindex(NMDevice *self, int ifindex, const char *ifname)
             nm_platform_link_set_user_ipv6ll_enabled(platform, priv->ip_ifindex, TRUE);
 
         if (!nm_platform_link_is_up(platform, priv->ip_ifindex))
-            nm_platform_link_set_up(platform, priv->ip_ifindex, NULL);
+            nm_platform_link_change_flags(platform, priv->ip_ifindex, IFF_UP, TRUE);
     }
 
     /* We don't care about any saved values from the old iface */
@@ -4870,7 +4838,6 @@ void
 nm_device_update_dynamic_ip_setup(NMDevice *self)
 {
     NMDevicePrivate *priv;
-    GError *         error = NULL;
 
     g_return_if_fail(NM_IS_DEVICE(self));
 
@@ -4903,17 +4870,6 @@ nm_device_update_dynamic_ip_setup(NMDevice *self)
     if (priv->dnsmasq_manager) {
         /* FIXME: todo */
     }
-
-    if (priv->lldp_listener && nm_lldp_listener_is_running(priv->lldp_listener)) {
-        nm_lldp_listener_stop(priv->lldp_listener);
-        if (!nm_lldp_listener_start(priv->lldp_listener, nm_device_get_ifindex(self), &error)) {
-            _LOGD(LOGD_DEVICE,
-                  "LLDP listener %p could not be restarted: %s",
-                  priv->lldp_listener,
-                  error->message);
-            g_clear_error(&error);
-        }
-    }
 }
 
 /*****************************************************************************/
@@ -5030,18 +4986,12 @@ nm_device_set_carrier(NMDevice *self, gboolean carrier)
 
     if (NM_FLAGS_ALL(priv->capabilities,
                      NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_NONSTANDARD_CARRIER)) {
-        if (carrier)
-            priv->interface_flags |= NM_DEVICE_INTERFACE_FLAG_CARRIER;
-        else
-            priv->interface_flags &= ~NM_DEVICE_INTERFACE_FLAG_CARRIER;
-        notify_flags = TRUE;
+        notify_flags = set_interface_flags(self, NM_DEVICE_INTERFACE_FLAG_CARRIER, carrier, FALSE);
     }
 
     priv->carrier = carrier;
-    if (notify_flags)
-        nm_gobject_notify_together(self, PROP_CARRIER, PROP_INTERFACE_FLAGS);
-    else
-        _notify(self, PROP_CARRIER);
+
+    nm_gobject_notify_together(self, PROP_CARRIER, notify_flags ? PROP_INTERFACE_FLAGS : PROP_0);
 
     if (priv->carrier) {
         _LOGI(LOGD_DEVICE, "carrier: link connected");
@@ -5216,11 +5166,11 @@ ndisc_set_router_config(NMNDisc *ndisc, NMDevice *self)
         if (addr->plen != 64)
             continue;
 
-        lifetime = nm_utils_lifetime_get(addr->timestamp,
-                                         addr->lifetime,
-                                         addr->preferred,
-                                         NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000,
-                                         &preferred);
+        lifetime = nmp_utils_lifetime_get(addr->timestamp,
+                                          addr->lifetime,
+                                          addr->preferred,
+                                          NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000,
+                                          &preferred);
         if (!lifetime)
             continue;
 
@@ -5272,6 +5222,8 @@ device_update_interface_flags(NMDevice *self, const NMPlatformLink *plink)
         flags |= NM_DEVICE_INTERFACE_FLAG_UP;
     if (plink && NM_FLAGS_HAS(plink->n_ifi_flags, IFF_LOWER_UP))
         flags |= NM_DEVICE_INTERFACE_FLAG_LOWER_UP;
+    if (plink && NM_FLAGS_HAS(plink->n_ifi_flags, IFF_PROMISC))
+        flags |= NM_DEVICE_INTERFACE_FLAG_PROMISC;
 
     if (NM_FLAGS_ALL(priv->capabilities,
                      NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_NONSTANDARD_CARRIER)) {
@@ -5282,10 +5234,12 @@ device_update_interface_flags(NMDevice *self, const NMPlatformLink *plink)
             flags |= NM_DEVICE_INTERFACE_FLAG_CARRIER;
     }
 
-    if (flags != priv->interface_flags) {
-        priv->interface_flags = flags;
-        _notify(self, PROP_INTERFACE_FLAGS);
-    }
+    set_interface_flags_full(self,
+                             NM_DEVICE_INTERFACE_FLAG_UP | NM_DEVICE_INTERFACE_FLAG_LOWER_UP
+                                 | NM_DEVICE_INTERFACE_FLAG_CARRIER
+                                 | NM_DEVICE_INTERFACE_FLAG_PROMISC,
+                             flags,
+                             TRUE);
 }
 
 static gboolean
@@ -5415,11 +5369,11 @@ device_link_changed(NMDevice *self)
                 /* Ensure the assume check is queued before any queued state changes
                  * from the transition to UNAVAILABLE.
                  */
-                nm_device_queue_recheck_assume(self);
                 reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
             }
         }
 
+        nm_device_queue_recheck_assume(self);
         nm_device_set_unmanaged_by_flags(self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason);
     }
 
@@ -5892,16 +5846,19 @@ sriov_op_queue(NMDevice *              self,
 static void
 device_init_static_sriov_num_vfs(NMDevice *self)
 {
-    NMDevicePrivate *priv  = NM_DEVICE_GET_PRIVATE(self);
-    gs_free char *   value = NULL;
-    int              num_vfs;
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
     if (priv->ifindex > 0 && nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) {
-        value   = nm_config_data_get_device_config(NM_CONFIG_GET_DATA,
-                                                 NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS,
-                                                 self,
-                                                 NULL);
-        num_vfs = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXINT32, -1);
+        int num_vfs;
+
+        num_vfs = nm_config_data_get_device_config_int64(NM_CONFIG_GET_DATA,
+                                                         NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS,
+                                                         self,
+                                                         10,
+                                                         0,
+                                                         G_MAXINT32,
+                                                         -1,
+                                                         -1);
         if (num_vfs >= 0)
             sriov_op_queue(self, num_vfs, NM_OPTION_BOOL_DEFAULT, NULL, NULL);
     }
@@ -7215,7 +7172,7 @@ nm_device_generate_connection(NMDevice *self,
 
     g_object_set(s_con,
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_buf(uuid),
+                 nm_uuid_generate_random_str_arr(uuid),
                  NM_SETTING_CONNECTION_ID,
                  ifname,
                  NM_SETTING_CONNECTION_AUTOCONNECT,
@@ -7467,11 +7424,10 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e
     s_match = (NMSettingMatch *) nm_connection_get_setting(connection, NM_TYPE_SETTING_MATCH);
     if (s_match) {
         const char *const *patterns;
-        const char *       device_driver;
         guint              num_patterns = 0;
 
         patterns = nm_setting_match_get_interface_names(s_match, &num_patterns);
-        if (!nm_wildcard_match_check(device_iface, patterns, num_patterns)) {
+        if (num_patterns > 0 && !nm_wildcard_match_check(device_iface, patterns, num_patterns)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
                                        "device does not satisfy match.interface-name property");
@@ -7486,9 +7442,9 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e
                                                     error))
             return FALSE;
 
-        device_driver = nm_device_get_driver(self);
-        patterns      = nm_setting_match_get_drivers(s_match, &num_patterns);
-        if (!nm_wildcard_match_check(device_driver, patterns, num_patterns)) {
+        patterns = nm_setting_match_get_drivers(s_match, &num_patterns);
+        if (num_patterns > 0
+            && !nm_wildcard_match_check(nm_device_get_driver(self), patterns, num_patterns)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
                                        "device does not satisfy match.driver property");
@@ -7496,7 +7452,7 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e
         }
 
         patterns = nm_setting_match_get_paths(s_match, &num_patterns);
-        if (!nm_wildcard_match_check(priv->path, patterns, num_patterns)) {
+        if (num_patterns > 0 && !nm_wildcard_match_check(priv->path, patterns, num_patterns)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
                                        "device does not satisfy match.path property");
@@ -7925,14 +7881,6 @@ master_ready_cb(NMActiveConnection *active, GParamSpec *pspec, NMDevice *self)
         nm_device_activate_schedule_stage1_device_prepare(self, FALSE);
 }
 
-static void
-lldp_neighbors_changed(NMLldpListener *lldp_listener, GParamSpec *pspec, gpointer user_data)
-{
-    NMDevice *self = NM_DEVICE(user_data);
-
-    _notify(self, PROP_LLDP_NEIGHBORS);
-}
-
 static NMPlatformVF *
 sriov_vf_config_to_platform(NMDevice *self, NMSriovVF *vf, GError **error)
 {
@@ -8220,38 +8168,56 @@ act_stage2_config(NMDevice *self, NMDeviceStateReason *out_failure_reason)
 }
 
 static void
-lldp_init(NMDevice *self, gboolean restart)
+_lldp_neighbors_changed_cb(NMLldpListener *lldp_listener, gpointer user_data)
+{
+    _notify(user_data, PROP_LLDP_NEIGHBORS);
+}
+
+static void
+lldp_setup(NMDevice *self, NMTernary enabled)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
+    int              ifindex;
+    gboolean         notify_lldp_neighbors  = FALSE;
+    gboolean         notify_interface_flags = FALSE;
 
-    if (priv->ifindex > 0 && _prop_get_connection_lldp(self)) {
-        gs_free_error GError *error = NULL;
+    ifindex = nm_device_get_ifindex(self);
 
-        if (priv->lldp_listener) {
-            if (restart && nm_lldp_listener_is_running(priv->lldp_listener))
-                nm_lldp_listener_stop(priv->lldp_listener);
-        } else {
-            priv->lldp_listener = nm_lldp_listener_new();
-            g_signal_connect(priv->lldp_listener,
-                             "notify::" NM_LLDP_LISTENER_NEIGHBORS,
-                             G_CALLBACK(lldp_neighbors_changed),
-                             self);
-        }
+    if (ifindex <= 0)
+        enabled = FALSE;
+    else if (enabled == NM_TERNARY_DEFAULT)
+        enabled = _prop_get_connection_lldp(self);
 
-        if (!nm_lldp_listener_is_running(priv->lldp_listener)) {
-            if (nm_lldp_listener_start(priv->lldp_listener, nm_device_get_ifindex(self), &error))
-                _LOGD(LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
-            else {
-                _LOGD(LOGD_DEVICE,
-                      "LLDP listener %p could not be started: %s",
-                      priv->lldp_listener,
-                      error->message);
-            }
+    if (priv->lldp_listener) {
+        if (!enabled || nm_lldp_listener_get_ifindex(priv->lldp_listener) != ifindex) {
+            nm_clear_pointer(&priv->lldp_listener, nm_lldp_listener_destroy);
+            notify_lldp_neighbors = TRUE;
         }
-    } else {
-        if (priv->lldp_listener)
-            nm_lldp_listener_stop(priv->lldp_listener);
     }
+
+    if (enabled && !priv->lldp_listener) {
+        gs_free_error GError *error = NULL;
+
+        priv->lldp_listener =
+            nm_lldp_listener_new(ifindex, _lldp_neighbors_changed_cb, self, &error);
+        if (!priv->lldp_listener) {
+            /* This really shouldn't happen. It's likely a bug. Investigate when this happens! */
+            _LOGW(LOGD_DEVICE,
+                  "LLDP listener for ifindex %d could not be started: %s",
+                  ifindex,
+                  error->message);
+        } else
+            notify_lldp_neighbors = TRUE;
+    }
+
+    notify_interface_flags = set_interface_flags(self,
+                                                 NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED,
+                                                 !!priv->lldp_listener,
+                                                 FALSE);
+
+    nm_gobject_notify_together(self,
+                               notify_lldp_neighbors ? PROP_LLDP_NEIGHBORS : PROP_0,
+                               notify_interface_flags ? PROP_INTERFACE_FLAGS : PROP_0);
 }
 
 /* set-mode can be:
@@ -8362,26 +8328,23 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode)
 static gboolean
 tc_commit(NMDevice *self)
 {
-    NMConnection *    connection          = NULL;
     gs_unref_ptrarray GPtrArray *qdiscs   = NULL;
     gs_unref_ptrarray GPtrArray *tfilters = NULL;
-    NMSettingTCConfig *          s_tc     = NULL;
+    NMSettingTCConfig *          s_tc;
     NMPlatform *                 platform;
     int                          ip_ifindex;
 
-    platform   = nm_device_get_platform(self);
-    connection = nm_device_get_applied_connection(self);
-    if (connection)
-        s_tc = nm_connection_get_setting_tc_config(connection);
+    s_tc = nm_device_get_applied_setting(self, NM_TYPE_SETTING_TC_CONFIG);
+    if (!s_tc)
+        return TRUE;
 
     ip_ifindex = nm_device_get_ip_ifindex(self);
     if (!ip_ifindex)
-        return s_tc == NULL;
+        return FALSE;
 
-    if (s_tc) {
-        qdiscs   = nm_utils_qdiscs_from_tc_setting(platform, s_tc, ip_ifindex);
-        tfilters = nm_utils_tfilters_from_tc_setting(platform, s_tc, ip_ifindex);
-    }
+    platform = nm_device_get_platform(self);
+    qdiscs   = nm_utils_qdiscs_from_tc_setting(platform, s_tc, ip_ifindex);
+    tfilters = nm_utils_tfilters_from_tc_setting(platform, s_tc, ip_ifindex);
 
     if (!nm_platform_qdisc_sync(platform, ip_ifindex, qdiscs))
         return FALSE;
@@ -8405,8 +8368,10 @@ activate_stage2_device_config(NMDevice *self)
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
     NMDeviceClass *  klass;
     NMActStageReturn ret;
+    NMSettingWired * s_wired;
     gboolean         no_firmware = FALSE;
     CList *          iter;
+    NMTernary        accept_all_mac_addresses;
 
     nm_device_state_changed(self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
@@ -8464,7 +8429,30 @@ activate_stage2_device_config(NMDevice *self)
             nm_device_queue_recheck_assume(info->slave);
     }
 
-    lldp_init(self, TRUE);
+    s_wired = nm_device_get_applied_setting(self, NM_TYPE_SETTING_WIRED);
+    accept_all_mac_addresses =
+        s_wired ? nm_setting_wired_get_accept_all_mac_addresses(s_wired) : NM_TERNARY_DEFAULT;
+    if (accept_all_mac_addresses != NM_TERNARY_DEFAULT) {
+        int ifindex = nm_device_get_ip_ifindex(self);
+
+        if (ifindex > 0) {
+            int ifi_flags =
+                nm_platform_link_get_ifi_flags(nm_device_get_platform(self), ifindex, IFF_PROMISC);
+
+            if (ifi_flags >= 0 && ((!!ifi_flags) != (!!accept_all_mac_addresses))) {
+                nm_platform_link_change_flags(nm_device_get_platform(self),
+                                              ifindex,
+                                              IFF_PROMISC,
+                                              !!accept_all_mac_addresses);
+                if (priv->promisc_reset == NM_OPTION_BOOL_DEFAULT)
+                    priv->promisc_reset = !accept_all_mac_addresses;
+            }
+        }
+    }
+
+    lldp_setup(self, NM_TERNARY_DEFAULT);
+
+    _commit_mtu(self, NULL);
 
     nm_device_activate_schedule_stage3_ip_config_start(self);
 }
@@ -8881,6 +8869,21 @@ ensure_con_ip_config(NMDevice *self, int addr_family)
 
 /*****************************************************************************/
 
+static const char *
+_device_get_dhcp_anycast_address(NMDevice *self)
+{
+    NMDeviceClass *klass;
+
+    nm_assert(NM_IS_DEVICE(self));
+
+    klass = NM_DEVICE_GET_CLASS(self);
+
+    if (klass->get_dhcp_anycast_address)
+        return klass->get_dhcp_anycast_address(self);
+
+    return NULL;
+}
+
 static void
 dhcp4_cleanup(NMDevice *self, CleanupType cleanup_type, gboolean release)
 {
@@ -9238,7 +9241,7 @@ dhcp4_fail(NMDevice *self, NMDhcpState dhcp_state)
 
     _LOGD(LOGD_DHCP4,
           "DHCPv4 failed (ip_state %s, was_active %d)",
-          _ip_state_to_string(priv->ip_state_4),
+          nm_device_ip_state_to_str(priv->ip_state_4),
           priv->dhcp_data_4.was_active);
 
     /* The client is always left running after a failure. */
@@ -9386,12 +9389,13 @@ dhcp4_start(NMDevice *self)
     gs_unref_bytes GBytes *hwaddr                  = NULL;
     gs_unref_bytes GBytes *bcast_hwaddr            = NULL;
     gs_unref_bytes GBytes *client_id               = NULL;
-    gs_free char *         mud_url_free            = NULL;
     NMConnection *         connection;
     NMSettingConnection *  s_con;
     GError *               error = NULL;
     const NMPlatformLink * pllink;
     const char *const *    reject_servers;
+    gboolean               request_broadcast;
+    const char *           str;
 
     connection = nm_device_get_applied_connection(self);
     g_return_val_if_fail(connection, FALSE);
@@ -9405,10 +9409,22 @@ dhcp4_start(NMDevice *self)
     nm_dbus_object_clear_and_unexport(&priv->dhcp_data_4.config);
     priv->dhcp_data_4.config = nm_dhcp_config_new(AF_INET);
 
+    request_broadcast = FALSE;
+
     pllink = nm_platform_link_get(nm_device_get_platform(self), nm_device_get_ip_ifindex(self));
     if (pllink) {
         hwaddr       = nmp_link_address_get_as_bytes(&pllink->l_address);
         bcast_hwaddr = nmp_link_address_get_as_bytes(&pllink->l_broadcast);
+
+        str = nmp_object_link_udev_device_get_property_value(NMP_OBJECT_UP_CAST(pllink),
+                                                             "ID_NET_DHCP_BROADCAST");
+        if (str && _nm_utils_ascii_str_to_bool(str, FALSE)) {
+            /* Use the device property ID_NET_DHCP_BROADCAST setting, which may be set for interfaces
+             * requiring that the DHCPOFFER message is being broadcast because they can't handle unicast
+             * messages while not fully configured.
+             */
+            request_broadcast = TRUE;
+        }
     }
 
     client_id = _prop_get_ipv4_dhcp_client_id(self, connection, hwaddr);
@@ -9417,28 +9433,29 @@ dhcp4_start(NMDevice *self)
     reject_servers = nm_setting_ip_config_get_dhcp_reject_servers(s_ip4, NULL);
 
     g_warn_if_fail(priv->dhcp_data_4.client == NULL);
-    priv->dhcp_data_4.client =
-        nm_dhcp_manager_start_ip4(nm_dhcp_manager_get(),
-                                  nm_netns_get_multi_idx(nm_device_get_netns(self)),
-                                  nm_device_get_ip_iface(self),
-                                  nm_device_get_ip_ifindex(self),
-                                  hwaddr,
-                                  bcast_hwaddr,
-                                  nm_connection_get_uuid(connection),
-                                  nm_device_get_route_table(self, AF_INET),
-                                  nm_device_get_route_metric(self, AF_INET),
-                                  nm_setting_ip_config_get_dhcp_send_hostname(s_ip4),
-                                  nm_setting_ip_config_get_dhcp_hostname(s_ip4),
-                                  nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip4)),
-                                  _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET),
-                                  _prop_get_connection_mud_url(self, s_con, &mud_url_free),
-                                  client_id,
-                                  _prop_get_ipvx_dhcp_timeout(self, AF_INET),
-                                  priv->dhcp_anycast_address,
-                                  NULL,
-                                  vendor_class_identifier,
-                                  reject_servers,
-                                  &error);
+    priv->dhcp_data_4.client = nm_dhcp_manager_start_ip4(
+        nm_dhcp_manager_get(),
+        nm_netns_get_multi_idx(nm_device_get_netns(self)),
+        nm_device_get_ip_iface(self),
+        nm_device_get_ip_ifindex(self),
+        hwaddr,
+        bcast_hwaddr,
+        nm_connection_get_uuid(connection),
+        nm_device_get_route_table(self, AF_INET),
+        nm_device_get_route_metric(self, AF_INET),
+        request_broadcast ? NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST : NM_DHCP_CLIENT_FLAGS_NONE,
+        nm_setting_ip_config_get_dhcp_send_hostname(s_ip4),
+        nm_setting_ip_config_get_dhcp_hostname(s_ip4),
+        nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip4)),
+        _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET),
+        _prop_get_connection_mud_url(self, s_con),
+        client_id,
+        _prop_get_ipvx_dhcp_timeout(self, AF_INET),
+        _device_get_dhcp_anycast_address(self),
+        NULL,
+        vendor_class_identifier,
+        reject_servers,
+        &error);
     if (!priv->dhcp_data_4.client) {
         _LOGW(LOGD_DHCP4, "failure to start DHCP: %s", error->message);
         g_clear_error(&error);
@@ -9669,7 +9686,7 @@ dhcp6_fail(NMDevice *self, NMDhcpState dhcp_state)
 
     _LOGD(LOGD_DHCP6,
           "DHCPv6 failed (ip_state %s, was_active %d)",
-          _ip_state_to_string(priv->ip_state_6),
+          nm_device_ip_state_to_str(priv->ip_state_6),
           priv->dhcp_data_6.was_active);
 
     /* The client is always left running after a failure. */
@@ -9835,8 +9852,7 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection)
     gs_unref_bytes GBytes *     duid         = NULL;
     gboolean                    enforce_duid = FALSE;
     const NMPlatformLink *      pllink;
-    gs_free char *              mud_url_free = NULL;
-    GError *                    error        = NULL;
+    GError *                    error = NULL;
     guint32                     iaid;
     gboolean                    iaid_explicit;
     NMSettingConnection *       s_con;
@@ -9850,9 +9866,9 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection)
     nm_assert(s_con);
 
     if (priv->ext_ip6_config_captured) {
-        ll_addr = nm_ip6_config_find_first_address(priv->ext_ip6_config_captured,
-                                                   NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-                                                       | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL);
+        ll_addr = nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured),
+                                                  NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
+                                                      | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL);
     }
 
     if (!ll_addr) {
@@ -9876,17 +9892,18 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection)
         nm_connection_get_uuid(connection),
         nm_device_get_route_table(self, AF_INET6),
         nm_device_get_route_metric(self, AF_INET6),
+        (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY
+                                                            : NM_DHCP_CLIENT_FLAGS_NONE,
         nm_setting_ip_config_get_dhcp_send_hostname(s_ip6),
         nm_setting_ip_config_get_dhcp_hostname(s_ip6),
         _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6),
-        _prop_get_connection_mud_url(self, s_con, &mud_url_free),
+        _prop_get_connection_mud_url(self, s_con),
         duid,
         enforce_duid,
         iaid,
         iaid_explicit,
         _prop_get_ipvx_dhcp_timeout(self, AF_INET6),
-        priv->dhcp_anycast_address,
-        (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
+        _device_get_dhcp_anycast_address(self),
         nm_setting_ip6_config_get_ip6_privacy(NM_SETTING_IP6_CONFIG(s_ip6)),
         priv->dhcp6.needed_prefixes,
         &error);
@@ -10090,9 +10107,9 @@ linklocal6_check_complete(NMDevice *self)
     }
 
     if (!priv->ext_ip6_config_captured
-        || !nm_ip6_config_find_first_address(priv->ext_ip6_config_captured,
-                                             NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-                                                 | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) {
+        || !nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured),
+                                            NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
+                                                | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) {
         /* we don't have a non-tentative link local address yet. Wait longer. */
         return;
     }
@@ -10138,10 +10155,10 @@ check_and_add_ipv6ll_addr(NMDevice *self)
         return;
 
     if (priv->ext_ip6_config_captured
-        && nm_ip6_config_find_first_address(priv->ext_ip6_config_captured,
-                                            NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-                                                | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL
-                                                | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) {
+        && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured),
+                                           NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
+                                               | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL
+                                               | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) {
         /* Already have an LL address, nothing to do */
         return;
     }
@@ -10211,9 +10228,9 @@ linklocal6_start(NMDevice *self)
     nm_clear_g_source(&priv->linklocal6_timeout_id);
 
     if (priv->ext_ip6_config_captured
-        && nm_ip6_config_find_first_address(priv->ext_ip6_config_captured,
-                                            NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-                                                | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL))
+        && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured),
+                                           NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
+                                               | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL))
         return TRUE;
 
     _LOGD(LOGD_DEVICE,
@@ -10458,9 +10475,9 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config)
             _LOGT(LOGD_DEVICE,
                   "mtu: value %u from source '%s' (%u), current source '%s' (%u)%s",
                   (guint) mtu,
-                  mtu_source_to_str(source),
+                  nm_device_mtu_source_to_str(source),
                   (guint) source,
-                  mtu_source_to_str(priv->mtu_source),
+                  nm_device_mtu_source_to_str(priv->mtu_source),
                   (guint) priv->mtu_source,
                   force ? " (forced)" : "");
         }
@@ -10792,9 +10809,9 @@ ndisc_ra_timeout(NMNDisc *ndisc, NMDevice *self)
          *        addresses we find inside priv->ip_config_6.
          */
         if (priv->ip_config_6
-            && nm_ip6_config_find_first_address(priv->ip_config_6,
-                                                NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL
-                                                    | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))
+            && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ip_config_6),
+                                               NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL
+                                                   | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))
             nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL);
         else
             nm_device_activate_schedule_ip_config_timeout(self, AF_INET6);
@@ -11397,10 +11414,10 @@ activate_stage3_ip_config_start(NMDevice *self)
 }
 
 static void
-fw_change_zone_cb(NMFirewallManager *      firewall_manager,
-                  NMFirewallManagerCallId *call_id,
-                  GError *                 error,
-                  gpointer                 user_data)
+fw_change_zone_cb(NMFirewalldManager *      firewalld_manager,
+                  NMFirewalldManagerCallId *call_id,
+                  GError *                  error,
+                  gpointer                  user_data)
 {
     NMDevice *       self = user_data;
     NMDevicePrivate *priv;
@@ -11452,12 +11469,12 @@ fw_change_zone(NMDevice *self)
     nm_assert(s_con);
 
     if (priv->fw_call) {
-        nm_firewall_manager_cancel_call(priv->fw_call);
+        nm_firewalld_manager_cancel_call(priv->fw_call);
         nm_assert(!priv->fw_call);
     }
 
     if (G_UNLIKELY(!priv->fw_mgr))
-        priv->fw_mgr = g_object_ref(nm_firewall_manager_get());
+        priv->fw_mgr = g_object_ref(nm_firewalld_manager_get());
 
     zone = nm_setting_connection_get_zone(s_con);
 #if WITH_FIREWALLD_ZONE
@@ -11469,12 +11486,12 @@ fw_change_zone(NMDevice *self)
             zone = "nm-shared";
     }
 #endif
-    priv->fw_call = nm_firewall_manager_add_or_change_zone(priv->fw_mgr,
-                                                           nm_device_get_ip_iface(self),
-                                                           zone,
-                                                           FALSE, /* change zone */
-                                                           fw_change_zone_cb,
-                                                           self);
+    priv->fw_call = nm_firewalld_manager_add_or_change_zone(priv->fw_mgr,
+                                                            nm_device_get_ip_iface(self),
+                                                            zone,
+                                                            FALSE, /* change zone */
+                                                            fw_change_zone_cb,
+                                                            self);
 }
 
 /*
@@ -11634,7 +11651,7 @@ share_init(NMDevice *self, GError **error)
     }
 
     for (i = 0; i < G_N_ELEMENTS(modules); i++)
-        nm_utils_modprobe(NULL, FALSE, modules[i], NULL);
+        nmp_utils_modprobe(NULL, FALSE, modules[i], NULL);
 
     return TRUE;
 }
@@ -11650,7 +11667,7 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
     NMConnection *              conn;
     NMSettingConnection *       s_con;
     gboolean                    announce_android_metered;
-    NMUtilsShareRules *         share_rules;
+    NMFirewallConfig *          firewall_config;
 
     g_return_val_if_fail(config, FALSE);
 
@@ -11675,13 +11692,9 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
     req = nm_device_get_act_request(self);
     g_return_val_if_fail(req, FALSE);
 
-    share_rules = nm_utils_share_rules_new();
-
-    nm_utils_share_rules_add_all_rules(share_rules, ip_iface, ip4_addr->address, ip4_addr->plen);
+    firewall_config = nm_firewall_config_new(ip_iface, ip4_addr->address, ip4_addr->plen);
 
-    nm_utils_share_rules_apply(share_rules, TRUE);
-
-    nm_act_request_set_shared(req, share_rules);
+    nm_act_request_set_shared(req, firewall_config);
 
     conn  = nm_act_request_get_applied_connection(req);
     s_con = nm_connection_get_setting_connection(conn);
@@ -11803,7 +11816,7 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family)
 
     if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex)
         && !nm_device_sys_iface_state_is_external_or_assume(self)) {
-        nm_platform_link_set_up(nm_device_get_platform(self), ip_ifindex, NULL);
+        nm_platform_link_change_flags(nm_device_get_platform(self), ip_ifindex, IFF_UP, TRUE);
         if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex))
             _LOGW(LOGD_DEVICE,
                   "interface %s not up for IP configuration",
@@ -12141,28 +12154,19 @@ nm_device_is_nm_owned(NMDevice *self)
 static gboolean
 delete_on_deactivate_link_delete(gpointer user_data)
 {
-    DeleteOnDeactivateData *data = user_data;
-    NMDevice *              self = data->device;
+    DeleteOnDeactivateData *data        = user_data;
+    nm_auto_unref_object NMDevice *self = data->device;
+    NMDevicePrivate *              priv = NM_DEVICE_GET_PRIVATE(self);
+    gs_free_error GError *error         = NULL;
 
     _LOGD(LOGD_DEVICE,
-          "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)",
-          data->ifindex,
+          "delete_on_deactivate: cleanup and delete virtual link (id=%u)",
           data->idle_add_id);
 
-    if (data->device) {
-        NMDevicePrivate *priv       = NM_DEVICE_GET_PRIVATE(data->device);
-        gs_free_error GError *error = NULL;
-
-        g_object_remove_weak_pointer(G_OBJECT(data->device), (void **) &data->device);
-        priv->delete_on_deactivate_data = NULL;
+    priv->delete_on_deactivate_data = NULL;
 
-        if (!nm_device_unrealize(data->device, TRUE, &error))
-            _LOGD(LOGD_DEVICE,
-                  "delete_on_deactivate: unrealizing %d failed (%s)",
-                  data->ifindex,
-                  error->message);
-    } else if (data->ifindex > 0)
-        nm_platform_link_delete(nm_device_get_platform(self), data->ifindex);
+    if (!nm_device_unrealize(self, TRUE, &error))
+        _LOGD(LOGD_DEVICE, "delete_on_deactivate: unrealizing failed (%s)", error->message);
 
     nm_device_emit_recheck_auto_activate(self);
 
@@ -12181,17 +12185,16 @@ delete_on_deactivate_unschedule(NMDevice *self)
         priv->delete_on_deactivate_data = NULL;
 
         g_source_remove(data->idle_add_id);
-        g_object_remove_weak_pointer(G_OBJECT(self), (void **) &data->device);
         _LOGD(LOGD_DEVICE,
-              "delete_on_deactivate: cancel cleanup and delete virtual link #%d (id=%u)",
-              data->ifindex,
+              "delete_on_deactivate: cancel cleanup and delete virtual link (id=%u)",
               data->idle_add_id);
+        g_object_unref(data->device);
         g_free(data);
     }
 }
 
 static void
-delete_on_deactivate_check_and_schedule(NMDevice *self, int ifindex)
+delete_on_deactivate_check_and_schedule(NMDevice *self)
 {
     NMDevicePrivate *       priv = NM_DEVICE_GET_PRIVATE(self);
     DeleteOnDeactivateData *data;
@@ -12208,16 +12211,13 @@ delete_on_deactivate_check_and_schedule(NMDevice *self, int ifindex)
         return;
     delete_on_deactivate_unschedule(self); /* always cancel and reschedule */
 
-    data = g_new(DeleteOnDeactivateData, 1);
-    g_object_add_weak_pointer(G_OBJECT(self), (void **) &data->device);
-    data->device                    = self;
-    data->ifindex                   = ifindex;
+    data                            = g_new(DeleteOnDeactivateData, 1);
+    data->device                    = g_object_ref(self);
     data->idle_add_id               = g_idle_add(delete_on_deactivate_link_delete, data);
     priv->delete_on_deactivate_data = data;
 
     _LOGD(LOGD_DEVICE,
-          "delete_on_deactivate: schedule cleanup and delete virtual link #%d (id=%u)",
-          ifindex,
+          "delete_on_deactivate: schedule cleanup and delete virtual link (id=%u)",
           data->idle_add_id);
 }
 
@@ -12696,7 +12696,7 @@ check_and_reapply_connection(NMDevice *    self,
     klass->reapply_connection(self, con_old, con_new);
 
     if (priv->state >= NM_DEVICE_STATE_CONFIG)
-        lldp_init(self, FALSE);
+        lldp_setup(self, NM_TERNARY_DEFAULT);
 
     if (priv->state >= NM_DEVICE_STATE_IP_CONFIG) {
         s_ip4_old = nm_connection_get_setting_ip4_config(con_old);
@@ -12927,9 +12927,10 @@ impl_device_get_applied_connection(NMDBusObject *                     obj,
         return;
     }
 
-    var_settings = nm_connection_to_dbus(applied_connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
+    var_settings =
+        nm_connection_to_dbus(applied_connection, NM_CONNECTION_SERIALIZE_WITH_NON_SECRET);
     if (!var_settings)
-        var_settings = g_variant_new_array(G_VARIANT_TYPE("{sa{sv}}"), NULL, 0);
+        var_settings = nm_g_variant_singleton_aLsaLsvII();
 
     g_dbus_method_invocation_return_value(
         invocation,
@@ -13478,12 +13479,13 @@ nm_device_set_ip_config(NMDevice *  self,
                         gboolean    commit,
                         GPtrArray * ip4_dev_route_blacklist)
 {
-    NMDevicePrivate *     priv    = NM_DEVICE_GET_PRIVATE(self);
-    const int             IS_IPv4 = NM_IS_IPv4(addr_family);
-    NMIPConfig *          old_config;
-    gboolean              has_changes = FALSE;
-    gboolean              success     = TRUE;
-    NMSettingsConnection *settings_connection;
+    NMDevicePrivate *      priv    = NM_DEVICE_GET_PRIVATE(self);
+    const int              IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMIPConfig *           old_config;
+    gboolean               has_changes = FALSE;
+    gboolean               success     = TRUE;
+    NMSettingsConnection * settings_connection;
+    NMIPRouteTableSyncMode route_table_sync_mode;
 
     nm_assert_addr_family(addr_family);
     nm_assert(!new_config || nm_ip_config_get_addr_family(new_config) == addr_family);
@@ -13495,11 +13497,18 @@ nm_device_set_ip_config(NMDevice *  self,
                   })));
     nm_assert(IS_IPv4 || !ip4_dev_route_blacklist);
 
+    if (commit && new_config)
+        route_table_sync_mode = _get_route_table_sync_mode_stateful(self, addr_family);
+    else
+        route_table_sync_mode = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE;
+
     _LOGD(LOGD_IPX(IS_IPv4),
-          "ip%c-config: update (commit=%d, new-config=%p)",
+          "ip%c-config: update (commit=%d, new-config=" NM_HASH_OBFUSCATE_PTR_FMT
+          ", route-table-sync-mode=%d)",
           nm_utils_addr_family_to_char(addr_family),
           commit,
-          new_config);
+          NM_HASH_OBFUSCATE_PTR(new_config),
+          (int) route_table_sync_mode);
 
     /* Always commit to nm-platform to update lifetimes */
     if (commit && new_config) {
@@ -13508,7 +13517,7 @@ nm_device_set_ip_config(NMDevice *  self,
         if (IS_IPv4) {
             success = nm_ip4_config_commit(NM_IP4_CONFIG(new_config),
                                            nm_device_get_platform(self),
-                                           _get_route_table_sync_mode_stateful(self, AF_INET));
+                                           route_table_sync_mode);
             nm_platform_ip4_dev_route_blacklist_set(nm_device_get_platform(self),
                                                     nm_ip_config_get_ifindex(new_config),
                                                     ip4_dev_route_blacklist);
@@ -13517,7 +13526,7 @@ nm_device_set_ip_config(NMDevice *  self,
 
             success = nm_ip6_config_commit(NM_IP6_CONFIG(new_config),
                                            nm_device_get_platform(self),
-                                           _get_route_table_sync_mode_stateful(self, AF_INET6),
+                                           route_table_sync_mode,
                                            &temporary_not_available);
 
             if (!_rt6_temporary_not_available_set(self, temporary_not_available))
@@ -13596,7 +13605,7 @@ nm_device_set_ip_config(NMDevice *  self,
                                           NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
                                           NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
                                           NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
-                                          NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
+                                          NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET,
                                           "update-external",
                                           NULL);
         }
@@ -14007,13 +14016,14 @@ nm_device_is_up(NMDevice *self)
 static gint64
 _get_carrier_wait_ms(NMDevice *self)
 {
-    gs_free char *value = NULL;
-
-    value = nm_config_data_get_device_config(NM_CONFIG_GET_DATA,
-                                             NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT,
-                                             self,
-                                             NULL);
-    return _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXINT32, CARRIER_WAIT_TIME_MS);
+    return nm_config_data_get_device_config_int64(NM_CONFIG_GET_DATA,
+                                                  NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT,
+                                                  self,
+                                                  10,
+                                                  0,
+                                                  G_MAXINT32,
+                                                  CARRIER_WAIT_TIME_MS,
+                                                  CARRIER_WAIT_TIME_MS);
 }
 
 gboolean
@@ -14023,6 +14033,7 @@ nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware)
     gboolean             device_is_up = FALSE;
     NMDeviceCapabilities capabilities;
     int                  ifindex;
+    int                  r;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
 
@@ -14038,7 +14049,9 @@ nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware)
     if (ifindex <= 0) {
         /* assume success. */
     } else {
-        if (!nm_platform_link_set_up(nm_device_get_platform(self), ifindex, no_firmware))
+        r = nm_platform_link_change_flags(nm_device_get_platform(self), ifindex, IFF_UP, TRUE);
+        NM_SET_OUT(no_firmware, (r == -NME_PL_NO_FIRMWARE));
+        if (r < 0)
             return FALSE;
     }
 
@@ -14125,7 +14138,7 @@ nm_device_take_down(NMDevice *self, gboolean block)
         return;
     }
 
-    if (!nm_platform_link_set_down(nm_device_get_platform(self), ifindex))
+    if (!nm_platform_link_change_flags(nm_device_get_platform(self), ifindex, IFF_UP, FALSE))
         return;
 
     device_is_up = nm_device_is_up(self);
@@ -14855,7 +14868,7 @@ _set_unmanaged_flags(NMDevice *          self,
           flags,
           NM_PRINT_FMT_QUOTED(allow_state_transition,
                               ", reason ",
-                              reason_to_string_a(reason),
+                              nm_device_state_reason_to_str_a(reason),
                               transition_state ? ", transition-state" : "",
                               ""));
 
@@ -15045,20 +15058,6 @@ nm_device_set_unmanaged_by_quitting(NMDevice *self)
 /*****************************************************************************/
 
 void
-nm_device_set_dhcp_anycast_address(NMDevice *self, const char *addr)
-{
-    NMDevicePrivate *priv;
-
-    g_return_if_fail(NM_IS_DEVICE(self));
-    g_return_if_fail(!addr || nm_utils_hwaddr_valid(addr, ETH_ALEN));
-
-    priv = NM_DEVICE_GET_PRIVATE(self);
-
-    g_free(priv->dhcp_anycast_address);
-    priv->dhcp_anycast_address = g_strdup(addr);
-}
-
-void
 nm_device_reapply_settings_immediately(NMDevice *self)
 {
     NMConnection *        applied_connection;
@@ -15730,7 +15729,7 @@ _cancel_activation(NMDevice *self)
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
     if (priv->fw_call) {
-        nm_firewall_manager_cancel_call(priv->fw_call);
+        nm_firewalld_manager_cancel_call(priv->fw_call);
         nm_assert(!priv->fw_call);
         priv->fw_call  = NULL;
         priv->fw_state = FIREWALL_STATE_INITIALIZED;
@@ -15760,11 +15759,11 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type)
 
     if (cleanup_type == CLEANUP_TYPE_DECONFIGURE && priv->fw_state >= FIREWALL_STATE_INITIALIZED
         && priv->fw_mgr && !nm_device_sys_iface_state_is_external(self)) {
-        nm_firewall_manager_remove_from_zone(priv->fw_mgr,
-                                             nm_device_get_ip_iface(self),
-                                             NULL,
-                                             NULL,
-                                             NULL);
+        nm_firewalld_manager_remove_from_zone(priv->fw_mgr,
+                                              nm_device_get_ip_iface(self),
+                                              NULL,
+                                              NULL,
+                                              NULL);
     }
     priv->fw_state = FIREWALL_STATE_UNMANAGED;
     g_clear_object(&priv->fw_mgr);
@@ -15846,7 +15845,7 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type)
         /* Check if the device was deactivated, and if so, delete_link.
          * Don't call delete_link synchronously because we are currently
          * handling a state change -- which is not reentrant. */
-        delete_on_deactivate_check_and_schedule(self, nm_device_get_ip_ifindex(self));
+        delete_on_deactivate_check_and_schedule(self);
     }
 
     /* ip_iface should be cleared after flushing all routes and addresses, since
@@ -15875,7 +15874,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
     else
         _LOGD(LOGD_DEVICE,
               "deactivating device (reason '%s') [%d]",
-              reason_to_string_a(reason),
+              nm_device_state_reason_to_str_a(reason),
               reason);
 
     /* Save whether or not we tried IPv6 for later */
@@ -15906,9 +15905,12 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
 
             nm_platform_ip_route_flush(platform, AF_UNSPEC, ifindex);
             nm_platform_ip_address_flush(platform, AF_UNSPEC, ifindex);
-            nm_platform_tfilter_sync(platform, ifindex, NULL);
-            nm_platform_qdisc_sync(platform, ifindex, NULL);
             set_ipv6_token(self, iid, "::");
+
+            if (nm_device_get_applied_setting(self, NM_TYPE_SETTING_TC_CONFIG)) {
+                nm_platform_tfilter_sync(platform, ifindex, NULL);
+                nm_platform_qdisc_sync(platform, ifindex, NULL);
+            }
         }
     }
 
@@ -15927,8 +15929,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
                                            FALSE,
                                            NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
-    if (priv->lldp_listener)
-        nm_lldp_listener_stop(priv->lldp_listener);
+    lldp_setup(self, NM_TERNARY_FALSE);
 
     nm_device_update_metered(self);
 
@@ -15979,6 +15980,14 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
 
     _ethtool_state_reset(self);
 
+    if (priv->promisc_reset != NM_OPTION_BOOL_DEFAULT && ifindex > 0) {
+        nm_platform_link_change_flags(nm_device_get_platform(self),
+                                      ifindex,
+                                      IFF_PROMISC,
+                                      !!priv->promisc_reset);
+        priv->promisc_reset = NM_OPTION_BOOL_DEFAULT;
+    }
+
     _cleanup_generic_post(self, cleanup_type);
 }
 
@@ -16093,7 +16102,8 @@ nm_device_spawn_iface_helper(NMDevice *self)
 
             hostname = nm_dhcp_client_get_hostname(priv->dhcp_data_4.client);
             if (hostname) {
-                if (nm_dhcp_client_get_use_fqdn(priv->dhcp_data_4.client))
+                if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(priv->dhcp_data_4.client),
+                                 NM_DHCP_CLIENT_FLAGS_USE_FQDN))
                     g_ptr_array_add(argv, g_strdup("--dhcp4-fqdn"));
                 else
                     g_ptr_array_add(argv, g_strdup("--dhcp4-hostname"));
@@ -16360,8 +16370,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
               "state change: %s -> %s (reason '%s', sys-iface-state: '%s'%s)",
               nm_device_state_to_str(old_state),
               nm_device_state_to_str(state),
-              reason_to_string_a(reason),
-              _sys_iface_state_to_str(priv->sys_iface_state),
+              nm_device_state_reason_to_str_a(reason),
+              nm_device_sys_iface_state_to_str(priv->sys_iface_state),
               priv->firmware_missing ? ", missing firmware" : "");
         return;
     }
@@ -16370,8 +16380,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
           "state change: %s -> %s (reason '%s', sys-iface-state: '%s')",
           nm_device_state_to_str(old_state),
           nm_device_state_to_str(state),
-          reason_to_string_a(reason),
-          _sys_iface_state_to_str(priv->sys_iface_state));
+          nm_device_state_reason_to_str_a(reason),
+          nm_device_sys_iface_state_to_str(priv->sys_iface_state));
 
     /* in order to prevent triggering any callback caused
      * by the device not having any pending action anymore
@@ -16717,7 +16727,7 @@ queued_state_set(gpointer user_data)
     _LOGD(LOGD_DEVICE,
           "queue-state[%s, reason:%s, id:%u]: %s",
           nm_device_state_to_str(priv->queued_state.state),
-          reason_to_string_a(priv->queued_state.reason),
+          nm_device_state_reason_to_str_a(priv->queued_state.reason),
           priv->queued_state.id,
           "change state");
 
@@ -16729,7 +16739,7 @@ queued_state_set(gpointer user_data)
     new_reason            = priv->queued_state.reason;
 
     nm_device_state_changed(self, new_state, new_reason);
-    nm_device_remove_pending_action(self, queued_state_to_string(new_state), TRUE);
+    nm_device_remove_pending_action(self, nm_device_state_queued_state_to_str(new_state), TRUE);
 
     return G_SOURCE_REMOVE;
 }
@@ -16747,12 +16757,12 @@ nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason r
         _LOGD(LOGD_DEVICE,
               "queue-state[%s, reason:%s, id:%u]: %s%s%s%s",
               nm_device_state_to_str(priv->queued_state.state),
-              reason_to_string_a(priv->queued_state.reason),
+              nm_device_state_reason_to_str_a(priv->queued_state.reason),
               priv->queued_state.id,
               "ignore queuing same state change",
               NM_PRINT_FMT_QUOTED(priv->queued_state.reason != reason,
                                   " (reason differs: ",
-                                  reason_to_string_a(reason),
+                                  nm_device_state_reason_to_str_a(reason),
                                   ")",
                                   ""));
         return;
@@ -16760,20 +16770,21 @@ nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason r
 
     /* Add pending action for the new state before clearing the queued states, so
      * that we don't accidentally pop all pending states and reach 'startup complete'  */
-    nm_device_add_pending_action(self, queued_state_to_string(state), TRUE);
+    nm_device_add_pending_action(self, nm_device_state_queued_state_to_str(state), TRUE);
 
     /* We should only ever have one delayed state transition at a time */
     if (priv->queued_state.id) {
         _LOGW(LOGD_DEVICE,
               "queue-state[%s, reason:%s, id:%u]: %s",
               nm_device_state_to_str(priv->queued_state.state),
-              reason_to_string_a(priv->queued_state.reason),
+              nm_device_state_reason_to_str_a(priv->queued_state.reason),
               priv->queued_state.id,
               "replace previously queued state change");
         nm_clear_g_source(&priv->queued_state.id);
-        nm_device_remove_pending_action(self,
-                                        queued_state_to_string(priv->queued_state.state),
-                                        TRUE);
+        nm_device_remove_pending_action(
+            self,
+            nm_device_state_queued_state_to_str(priv->queued_state.state),
+            TRUE);
     }
 
     priv->queued_state.state  = state;
@@ -16783,7 +16794,7 @@ nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason r
     _LOGD(LOGD_DEVICE,
           "queue-state[%s, reason:%s, id:%u]: %s",
           nm_device_state_to_str(state),
-          reason_to_string_a(reason),
+          nm_device_state_reason_to_str_a(reason),
           priv->queued_state.id,
           "queue state change");
 }
@@ -16799,11 +16810,13 @@ queued_state_clear(NMDevice *self)
     _LOGD(LOGD_DEVICE,
           "queue-state[%s, reason:%s, id:%u]: %s",
           nm_device_state_to_str(priv->queued_state.state),
-          reason_to_string_a(priv->queued_state.reason),
+          nm_device_state_reason_to_str_a(priv->queued_state.reason),
           priv->queued_state.id,
           "clear queued state change");
     nm_clear_g_source(&priv->queued_state.id);
-    nm_device_remove_pending_action(self, queued_state_to_string(priv->queued_state.state), TRUE);
+    nm_device_remove_pending_action(self,
+                                    nm_device_state_queued_state_to_str(priv->queued_state.state),
+                                    TRUE);
 }
 
 NMDeviceState
@@ -16821,7 +16834,7 @@ const char *
 nm_device_get_hw_address(NMDevice *self)
 {
     NMDevicePrivate *priv;
-    char             buf[NM_UTILS_HWADDR_LEN_MAX];
+    char             buf[_NM_UTILS_HWADDR_LEN_MAX];
     gsize            l;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), NULL);
@@ -16915,7 +16928,7 @@ void
 nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_freeze)
 {
     NMDevicePrivate *              priv = NM_DEVICE_GET_PRIVATE(self);
-    guint8                         buf[NM_UTILS_HWADDR_LEN_MAX];
+    guint8                         buf[_NM_UTILS_HWADDR_LEN_MAX];
     size_t                         len = 0;
     gboolean                       success_read;
     int                            ifindex;
@@ -17033,7 +17046,7 @@ _hw_addr_set(NMDevice *        self,
     NMDevicePrivate *priv;
     gboolean         success = FALSE;
     int              r;
-    guint8           addr_bytes[NM_UTILS_HWADDR_LEN_MAX];
+    guint8           addr_bytes[_NM_UTILS_HWADDR_LEN_MAX];
     gsize            addr_len;
     gboolean         was_taken_down = FALSE;
     gboolean         retry_down;
@@ -17234,14 +17247,13 @@ _hw_addr_get_cloned(NMDevice *    self,
                     gboolean *    preserve,
                     char **       hwaddr,
                     HwAddrType *  hwaddr_type,
-                    char **       hwaddr_detail,
+                    const char ** hwaddr_detail,
                     GError **     error)
 {
     NMDevicePrivate *priv;
-    gs_free char *   addr_setting_free             = NULL;
-    gs_free char *   hw_addr_generated             = NULL;
-    gs_free char *   generate_mac_address_mask_tmp = NULL;
-    const char *     addr, *addr_setting;
+    gs_free char *   hw_addr_generated = NULL;
+    const char *     addr;
+    const char *     addr_setting;
     char *           addr_out;
     HwAddrType       type_out;
 
@@ -17254,15 +17266,16 @@ _hw_addr_get_cloned(NMDevice *    self,
     if (!connection)
         g_return_val_if_reached(FALSE);
 
-    addr = addr_setting =
-        _prop_get_x_cloned_mac_address(self, connection, is_wifi, &addr_setting_free);
+    addr_setting = _prop_get_x_cloned_mac_address(self, connection, is_wifi);
+
+    addr = addr_setting;
 
     if (nm_streq(addr, NM_CLONED_MAC_PRESERVE)) {
         /* "preserve" means to reset the initial MAC address. */
         NM_SET_OUT(preserve, TRUE);
         NM_SET_OUT(hwaddr, NULL);
         NM_SET_OUT(hwaddr_type, HW_ADDR_TYPE_UNSET);
-        NM_SET_OUT(hwaddr_detail, g_steal_pointer(&addr_setting_free) ?: g_strdup(addr_setting));
+        NM_SET_OUT(hwaddr_detail, addr_setting);
         return TRUE;
     }
 
@@ -17275,8 +17288,7 @@ _hw_addr_get_cloned(NMDevice *    self,
             NM_SET_OUT(preserve, TRUE);
             NM_SET_OUT(hwaddr, NULL);
             NM_SET_OUT(hwaddr_type, HW_ADDR_TYPE_UNSET);
-            NM_SET_OUT(hwaddr_detail,
-                       g_steal_pointer(&addr_setting_free) ?: g_strdup(addr_setting));
+            NM_SET_OUT(hwaddr_detail, addr_setting);
             return TRUE;
         } else if (!addr) {
             g_set_error_literal(error,
@@ -17296,10 +17308,7 @@ _hw_addr_get_cloned(NMDevice *    self,
         }
         hw_addr_generated = nm_utils_hw_addr_gen_random_eth(
             nm_device_get_initial_hw_address(self),
-            _prop_get_x_generate_mac_address_mask(self,
-                                                  connection,
-                                                  is_wifi,
-                                                  &generate_mac_address_mask_tmp));
+            _prop_get_x_generate_mac_address_mask(self, connection, is_wifi));
         if (!hw_addr_generated) {
             g_set_error(error,
                         NM_DEVICE_ERROR,
@@ -17327,10 +17336,7 @@ _hw_addr_get_cloned(NMDevice *    self,
             stable_id,
             nm_device_get_ip_iface(self),
             nm_device_get_initial_hw_address(self),
-            _prop_get_x_generate_mac_address_mask(self,
-                                                  connection,
-                                                  is_wifi,
-                                                  &generate_mac_address_mask_tmp));
+            _prop_get_x_generate_mac_address_mask(self, connection, is_wifi));
         if (!hw_addr_generated) {
             g_set_error(error,
                         NM_DEVICE_ERROR,
@@ -17354,7 +17360,7 @@ _hw_addr_get_cloned(NMDevice *    self,
     NM_SET_OUT(preserve, FALSE);
     NM_SET_OUT(hwaddr, addr_out);
     NM_SET_OUT(hwaddr_type, type_out);
-    NM_SET_OUT(hwaddr_detail, g_steal_pointer(&addr_setting_free) ?: g_strdup(addr_setting));
+    NM_SET_OUT(hwaddr_detail, addr_setting);
     return TRUE;
 out_no_action:
     NM_SET_OUT(preserve, FALSE);
@@ -17384,7 +17390,7 @@ nm_device_hw_addr_set_cloned(NMDevice *self, NMConnection *connection, gboolean
     NMDevicePrivate *priv;
     gboolean         preserve   = FALSE;
     gs_free char *   hwaddr     = NULL;
-    gs_free char *   detail     = NULL;
+    const char *     detail     = NULL;
     HwAddrType       type       = HW_ADDR_TYPE_UNSET;
     gs_free_error GError *error = NULL;
 
@@ -17624,23 +17630,38 @@ hostname_dns_lookup_callback(GObject *source, GAsyncResult *result, gpointer use
 {
     HostnameResolver *resolver;
     NMDevice *        self;
-    gs_free char *    hostname  = NULL;
     gs_free char *    addr_str  = NULL;
+    gs_free char *    output    = NULL;
     gs_free_error GError *error = NULL;
 
-    hostname = g_resolver_lookup_by_address_finish(G_RESOLVER(source), result, &error);
+    output = nm_device_resolve_address_finish(result, &error);
     if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
         return;
 
-    resolver           = user_data;
-    self               = resolver->device;
-    resolver->state    = RESOLVER_DONE;
-    resolver->hostname = g_strdup(hostname);
+    resolver        = user_data;
+    self            = resolver->device;
+    resolver->state = RESOLVER_DONE;
 
-    _LOGD(LOGD_DNS,
-          "hostname-from-dns: lookup done for %s, result %s%s%s",
-          (addr_str = g_inet_address_to_string(resolver->address)),
-          NM_PRINT_FMT_QUOTE_STRING(hostname));
+    if (error) {
+        _LOGD(LOGD_DNS,
+              "hostname-from-dns: lookup error for %s: %s",
+              (addr_str = g_inet_address_to_string(resolver->address)),
+              error->message);
+    } else {
+        gboolean valid;
+
+        resolver->hostname = g_steal_pointer(&output);
+        valid              = nm_hostname_manager_validate_hostname(resolver->hostname);
+
+        _LOGD(LOGD_DNS,
+              "hostname-from-dns: lookup done for %s, result %s%s%s%s",
+              (addr_str = g_inet_address_to_string(resolver->address)),
+              NM_PRINT_FMT_QUOTE_STRING(resolver->hostname),
+              valid ? "" : " (invalid)");
+
+        if (!valid)
+            g_clear_pointer(&resolver->hostname, g_free);
+    }
 
     nm_clear_g_cancellable(&resolver->cancellable);
     g_signal_emit(self, signals[DNS_LOOKUP_DONE], 0);
@@ -17738,7 +17759,6 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean
     if (!resolver) {
         resolver  = g_slice_new(HostnameResolver);
         *resolver = (HostnameResolver){
-            .resolver    = g_resolver_get_default(),
             .device      = self,
             .addr_family = addr_family,
             .state       = RESOLVER_WAIT_ADDRESS,
@@ -17746,13 +17766,36 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean
         priv->hostname_resolver_x[IS_IPv4] = resolver;
     }
 
-    /* Determine the first address of the interface and
-     * whether it changed from the previous lookup */
+    /* Determine the most suitable address of the interface
+     * and whether it changed from the previous lookup */
     ip_config = priv->ip_config_x[IS_IPv4];
     if (ip_config) {
-        const NMPlatformIPAddress *addr;
+        const NMPlatformIPAddress *addr = NULL;
+
+        if (IS_IPv4) {
+            addr = nm_ip_config_get_first_address(ip_config);
+        } else {
+            /* For IPv6 prefer, in order:
+             * - !link-local, !deprecated
+             * - !link-local, deprecated
+             * - link-local
+             */
+            addr = nm_ip_config_find_first_address(ip_config,
+                                                   NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL
+                                                       | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL);
+            if (!addr) {
+                addr = nm_ip_config_find_first_address(
+                    ip_config,
+                    NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL
+                        | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED);
+            }
+            if (!addr) {
+                addr = nm_ip_config_find_first_address(ip_config,
+                                                       NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
+                                                           | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY);
+            }
+        }
 
-        addr = nm_ip_config_get_first_address(ip_config);
         if (addr) {
             new_address = g_inet_address_new_from_bytes(addr->address_ptr,
                                                         IS_IPv4 ? G_SOCKET_FAMILY_IPV4
@@ -17788,20 +17831,15 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean
     }
 
     if (address_changed && new_address) {
-        gs_free char *str = NULL;
-
-        _LOGT(LOGD_DNS,
-              "hostname-from-dns: starting lookup for address %s",
-              (str = g_inet_address_to_string(new_address)));
-
         resolver->state       = RESOLVER_IN_PROGRESS;
         resolver->cancellable = g_cancellable_new();
         resolver->address     = g_steal_pointer(&new_address);
-        g_resolver_lookup_by_address_async(resolver->resolver,
-                                           resolver->address,
-                                           resolver->cancellable,
-                                           hostname_dns_lookup_callback,
-                                           resolver);
+
+        nm_device_resolve_address(addr_family,
+                                  g_inet_address_to_bytes(resolver->address),
+                                  resolver->cancellable,
+                                  hostname_dns_lookup_callback,
+                                  resolver);
         nm_clear_g_source(&resolver->timeout_id);
     }
 
@@ -17851,7 +17889,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
     NMDevice *       self = NM_DEVICE(object);
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
-    GVariantBuilder  array_builder;
 
     switch (prop_id) {
     case PROP_UDI:
@@ -18002,12 +18039,10 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_uint(value, priv->metered);
         break;
     case PROP_LLDP_NEIGHBORS:
-        if (priv->lldp_listener)
-            g_value_set_variant(value, nm_lldp_listener_get_neighbors(priv->lldp_listener));
-        else {
-            g_variant_builder_init(&array_builder, G_VARIANT_TYPE("aa{sv}"));
-            g_value_take_variant(value, g_variant_builder_end(&array_builder));
-        }
+        g_value_set_variant(value,
+                            priv->lldp_listener
+                                ? nm_lldp_listener_get_neighbors(priv->lldp_listener)
+                                : nm_g_variant_singleton_aaLsvI());
         break;
     case PROP_REAL:
         g_value_set_boolean(value, nm_device_is_real(self));
@@ -18187,6 +18222,8 @@ nm_device_init(NMDevice *self)
 
     priv->v4_commit_first_time = TRUE;
     priv->v6_commit_first_time = TRUE;
+
+    priv->promisc_reset = NM_OPTION_BOOL_DEFAULT;
 }
 
 static GObject *
@@ -18216,7 +18253,7 @@ constructor(GType type, guint n_construct_params, GObjectConstructParam *constru
     }
 
     if (priv->hw_addr_perm) {
-        guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
+        guint8 buf[_NM_UTILS_HWADDR_LEN_MAX];
         gsize  l;
 
         if (!_nm_utils_hwaddr_aton(priv->hw_addr_perm, buf, sizeof(buf), &l)) {
@@ -18364,13 +18401,7 @@ dispose(GObject *object)
     nm_clear_g_source(&priv->device_link_changed_id);
     nm_clear_g_source(&priv->device_ip_link_changed_id);
 
-    if (priv->lldp_listener) {
-        g_signal_handlers_disconnect_by_func(priv->lldp_listener,
-                                             G_CALLBACK(lldp_neighbors_changed),
-                                             self);
-        nm_lldp_listener_stop(priv->lldp_listener);
-        g_clear_object(&priv->lldp_listener);
-    }
+    lldp_setup(self, FALSE);
 
     nm_clear_g_source(&priv->concheck_x[0].p_cur_id);
     nm_clear_g_source(&priv->concheck_x[1].p_cur_id);
@@ -18413,7 +18444,6 @@ finalize(GObject *object)
     g_free(priv->driver_version);
     g_free(priv->firmware_version);
     g_free(priv->type_desc);
-    g_free(priv->dhcp_anycast_address);
     g_free(priv->current_stable_id);
 
     g_hash_table_unref(priv->ip6_saved_properties);
@@ -18468,75 +18498,69 @@ static const NMDBusInterfaceInfoExtended interface_info_device = {
                                                 .handle = impl_device_delete, ), ),
         .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_state_changed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Udi", "s", NM_DEVICE_UDI),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Path", "s", NM_DEVICE_PATH),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Interface", "s", NM_DEVICE_IFACE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("IpInterface",
-                                                             "s",
-                                                             NM_DEVICE_IP_IFACE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Driver", "s", NM_DEVICE_DRIVER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("DriverVersion",
-                                                             "s",
-                                                             NM_DEVICE_DRIVER_VERSION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("FirmwareVersion",
-                                                             "s",
-                                                             NM_DEVICE_FIRMWARE_VERSION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Capabilities",
-                                                             "u",
-                                                             NM_DEVICE_CAPABILITIES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ip4Address",
-                                                             "u",
-                                                             NM_DEVICE_IP4_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("State", "u", NM_DEVICE_STATE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("StateReason",
-                                                             "(uu)",
-                                                             NM_DEVICE_STATE_REASON),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ActiveConnection",
-                                                             "o",
-                                                             NM_DEVICE_ACTIVE_CONNECTION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ip4Config",
-                                                             "o",
-                                                             NM_DEVICE_IP4_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Dhcp4Config",
-                                                             "o",
-                                                             NM_DEVICE_DHCP4_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ip6Config",
-                                                             "o",
-                                                             NM_DEVICE_IP6_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Dhcp6Config",
-                                                             "o",
-                                                             NM_DEVICE_DHCP6_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L("Managed",
-                                                                 "b",
-                                                                 NM_DEVICE_MANAGED,
-                                                                 NM_AUTH_PERMISSION_NETWORK_CONTROL,
-                                                                 NM_AUDIT_OP_DEVICE_MANAGED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L("Autoconnect",
-                                                                 "b",
-                                                                 NM_DEVICE_AUTOCONNECT,
-                                                                 NM_AUTH_PERMISSION_NETWORK_CONTROL,
-                                                                 NM_AUDIT_OP_DEVICE_AUTOCONNECT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("FirmwareMissing",
-                                                             "b",
-                                                             NM_DEVICE_FIRMWARE_MISSING),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("NmPluginMissing",
-                                                             "b",
-                                                             NM_DEVICE_NM_PLUGIN_MISSING),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("DeviceType",
-                                                             "u",
-                                                             NM_DEVICE_DEVICE_TYPE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("AvailableConnections",
-                                                             "ao",
-                                                             NM_DEVICE_AVAILABLE_CONNECTIONS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("PhysicalPortId",
-                                                             "s",
-                                                             NM_DEVICE_PHYSICAL_PORT_ID),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mtu", "u", NM_DEVICE_MTU),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Metered", "u", NM_DEVICE_METERED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("LldpNeighbors",
-                                                             "aa{sv}",
-                                                             NM_DEVICE_LLDP_NEIGHBORS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Real", "b", NM_DEVICE_REAL),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Udi", "s", NM_DEVICE_UDI),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Path", "s", NM_DEVICE_PATH),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Interface", "s", NM_DEVICE_IFACE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("IpInterface", "s", NM_DEVICE_IP_IFACE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Driver", "s", NM_DEVICE_DRIVER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DriverVersion",
+                                                           "s",
+                                                           NM_DEVICE_DRIVER_VERSION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FirmwareVersion",
+                                                           "s",
+                                                           NM_DEVICE_FIRMWARE_VERSION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Capabilities",
+                                                           "u",
+                                                           NM_DEVICE_CAPABILITIES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ip4Address",
+                                                           "u",
+                                                           NM_DEVICE_IP4_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("State", "u", NM_DEVICE_STATE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("StateReason",
+                                                           "(uu)",
+                                                           NM_DEVICE_STATE_REASON),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ActiveConnection",
+                                                           "o",
+                                                           NM_DEVICE_ACTIVE_CONNECTION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ip4Config", "o", NM_DEVICE_IP4_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Dhcp4Config",
+                                                           "o",
+                                                           NM_DEVICE_DHCP4_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ip6Config", "o", NM_DEVICE_IP6_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Dhcp6Config",
+                                                           "o",
+                                                           NM_DEVICE_DHCP6_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE("Managed",
+                                                               "b",
+                                                               NM_DEVICE_MANAGED,
+                                                               NM_AUTH_PERMISSION_NETWORK_CONTROL,
+                                                               NM_AUDIT_OP_DEVICE_MANAGED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE("Autoconnect",
+                                                               "b",
+                                                               NM_DEVICE_AUTOCONNECT,
+                                                               NM_AUTH_PERMISSION_NETWORK_CONTROL,
+                                                               NM_AUDIT_OP_DEVICE_AUTOCONNECT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FirmwareMissing",
+                                                           "b",
+                                                           NM_DEVICE_FIRMWARE_MISSING),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NmPluginMissing",
+                                                           "b",
+                                                           NM_DEVICE_NM_PLUGIN_MISSING),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DeviceType",
+                                                           "u",
+                                                           NM_DEVICE_DEVICE_TYPE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AvailableConnections",
+                                                           "ao",
+                                                           NM_DEVICE_AVAILABLE_CONNECTIONS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("PhysicalPortId",
+                                                           "s",
+                                                           NM_DEVICE_PHYSICAL_PORT_ID),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mtu", "u", NM_DEVICE_MTU),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Metered", "u", NM_DEVICE_METERED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("LldpNeighbors",
+                                                           "aa{sv}",
+                                                           NM_DEVICE_LLDP_NEIGHBORS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Real", "b", NM_DEVICE_REAL),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ip4Connectivity",
                                                            "u",
                                                            NM_DEVICE_IP4_CONNECTIVITY),
@@ -18551,10 +18575,9 @@ static const NMDBusInterfaceInfoExtended interface_info_device = {
                                                            NM_DEVICE_HW_ADDRESS), ), ),
 };
 
-const NMDBusInterfaceInfoExtended nm_interface_info_device_statistics = {
+static const NMDBusInterfaceInfoExtended interface_info_device_statistics = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_STATISTICS,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(
                 "RefreshRateMs",
@@ -18580,7 +18603,7 @@ nm_device_class_init(NMDeviceClass *klass)
 
     dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/Devices");
     dbus_object_class->interface_infos =
-        NM_DBUS_INTERFACE_INFOS(&interface_info_device, &nm_interface_info_device_statistics);
+        NM_DBUS_INTERFACE_INFOS(&interface_info_device, &interface_info_device_statistics);
 
     object_class->dispose      = dispose;
     object_class->finalize     = finalize;
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index 72777d0a..53211c6f 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -15,54 +15,7 @@
 #include "nm-connection.h"
 #include "nm-rfkill-manager.h"
 #include "NetworkManagerUtils.h"
-
-typedef enum _nm_packed {
-    NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
-    NM_DEVICE_SYS_IFACE_STATE_ASSUME,
-    NM_DEVICE_SYS_IFACE_STATE_MANAGED,
-
-    /* the REMOVED state applies when the device is manually set to unmanaged
-     * or the link was externally removed. In both cases, we move the device
-     * to UNMANAGED state, without touching the link -- be it, because the link
-     * is already gone or because we want to release it (give it up).
-     */
-    NM_DEVICE_SYS_IFACE_STATE_REMOVED,
-} NMDeviceSysIfaceState;
-
-typedef enum {
-    NM_DEVICE_MTU_SOURCE_NONE,
-    NM_DEVICE_MTU_SOURCE_PARENT,
-    NM_DEVICE_MTU_SOURCE_IP_CONFIG,
-    NM_DEVICE_MTU_SOURCE_CONNECTION,
-} NMDeviceMtuSource;
-
-static inline NMDeviceStateReason
-nm_device_state_reason_check(NMDeviceStateReason reason)
-{
-    /* the device-state-reason serves mostly informational purpose during a state
-     * change. In some cases however, decisions are made based on the reason.
-     * I tend to think that interpreting the state reason to derive some behaviors
-     * is confusing, because the cause and effect are so far apart.
-     *
-     * This function is here to mark source that inspects the reason to make
-     * a decision -- contrary to places that set the reason. Thus, by grepping
-     * for nm_device_state_reason_check() you can find the "effect" to a certain
-     * reason.
-     */
-    return reason;
-}
-
-#define NM_PENDING_ACTION_AUTOACTIVATE           "autoactivate"
-#define NM_PENDING_ACTION_IN_STATE_CHANGE        "in-state-change"
-#define NM_PENDING_ACTION_RECHECK_AVAILABLE      "recheck-available"
-#define NM_PENDING_ACTION_CARRIER_WAIT           "carrier-wait"
-#define NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT "waiting-for-supplicant"
-#define NM_PENDING_ACTION_WIFI_SCAN              "wifi-scan"
-#define NM_PENDING_ACTION_WAITING_FOR_COMPANION  "waiting-for-companion"
-#define NM_PENDING_ACTION_LINK_INIT              "link-init"
-
-#define NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "queued-state-change-"
-#define NM_PENDING_ACTIONPREFIX_ACTIVATION          "activation-"
+#include "nm-device-utils.h"
 
 /* Properties */
 #define NM_DEVICE_UDI                   "udi"
@@ -109,10 +62,10 @@ nm_device_state_reason_check(NMDeviceStateReason reason)
  * NMDeviceOvsBridge and NMDeviceOvsPort. */
 #define NM_DEVICE_SLAVES "slaves" /* partially internal */
 
-#define NM_DEVICE_TYPE_DESC          "type-desc" /* Internal only */
-#define NM_DEVICE_RFKILL_TYPE        "rfkill-type" /* Internal only */
-#define NM_DEVICE_IFINDEX            "ifindex" /* Internal only */
-#define NM_DEVICE_MASTER             "master" /* Internal only */
+#define NM_DEVICE_TYPE_DESC          "type-desc"          /* Internal only */
+#define NM_DEVICE_RFKILL_TYPE        "rfkill-type"        /* Internal only */
+#define NM_DEVICE_IFINDEX            "ifindex"            /* Internal only */
+#define NM_DEVICE_MASTER             "master"             /* Internal only */
 #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */
 
 /* Internal signals */
@@ -459,6 +412,8 @@ typedef struct _NMDeviceClass {
 
     gboolean (*set_platform_mtu)(NMDevice *self, guint32 mtu);
 
+    const char *(*get_dhcp_anycast_address)(NMDevice *self);
+
 } NMDeviceClass;
 
 GType nm_device_get_type(void);
@@ -627,27 +582,28 @@ void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device);
  *   setting the NM_UNMANAGED_IS_SLAVE to %TRUE makes no sense, this flag has only
  *   meaning to set a slave device as managed if the parent is managed too.
  */
-typedef enum { /*< skip >*/
-               NM_UNMANAGED_NONE = 0,
+typedef enum {
+    NM_UNMANAGED_NONE = 0,
 
-               /* these flags are authoritative. If one of them is set,
+    /* these flags are authoritative. If one of them is set,
      * the device cannot be managed. */
-               NM_UNMANAGED_SLEEPING      = (1LL << 0),
-               NM_UNMANAGED_QUITTING      = (1LL << 1),
-               NM_UNMANAGED_PARENT        = (1LL << 2),
-               NM_UNMANAGED_BY_TYPE       = (1LL << 3),
-               NM_UNMANAGED_PLATFORM_INIT = (1LL << 4),
-               NM_UNMANAGED_USER_EXPLICIT = (1LL << 5),
-               NM_UNMANAGED_USER_SETTINGS = (1LL << 6),
-
-               /* These flags can be non-effective and be overwritten
+    NM_UNMANAGED_SLEEPING      = (1LL << 0),
+    NM_UNMANAGED_QUITTING      = (1LL << 1),
+    NM_UNMANAGED_PARENT        = (1LL << 2),
+    NM_UNMANAGED_BY_TYPE       = (1LL << 3),
+    NM_UNMANAGED_PLATFORM_INIT = (1LL << 4),
+    NM_UNMANAGED_USER_EXPLICIT = (1LL << 5),
+    NM_UNMANAGED_USER_SETTINGS = (1LL << 6),
+
+    /* These flags can be non-effective and be overwritten
      * by other flags. */
-               NM_UNMANAGED_BY_DEFAULT    = (1LL << 8),
-               NM_UNMANAGED_USER_CONF     = (1LL << 9),
-               NM_UNMANAGED_USER_UDEV     = (1LL << 10),
-               NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 11),
-               NM_UNMANAGED_IS_SLAVE      = (1LL << 12),
+    NM_UNMANAGED_BY_DEFAULT    = (1LL << 7),
+    NM_UNMANAGED_USER_CONF     = (1LL << 8),
+    NM_UNMANAGED_USER_UDEV     = (1LL << 9),
+    NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 10),
+    NM_UNMANAGED_IS_SLAVE      = (1LL << 11),
 
+    NM_UNMANAGED_ALL = ((1LL << 12) - 1),
 } NMUnmanagedFlags;
 
 typedef enum {
@@ -861,9 +817,6 @@ struct _NMBtVTableNetworkServer {
     gboolean (*unregister_bridge)(const NMBtVTableNetworkServer *vtable, NMDevice *device);
 };
 
-const char *nm_device_state_to_str(NMDeviceState state);
-const char *nm_device_state_reason_to_str(NMDeviceStateReason reason);
-
 gboolean nm_device_is_vpn(NMDevice *self);
 
 const char *
diff --git a/src/core/devices/nm-lldp-listener.c b/src/core/devices/nm-lldp-listener.c
index c60fb3ad..aba972ae 100644
--- a/src/core/devices/nm-lldp-listener.c
+++ b/src/core/devices/nm-lldp-listener.c
@@ -9,12 +9,15 @@
 
 #include <net/ethernet.h>
 
-#include "nm-std-aux/unaligned.h"
-#include "platform/nm-platform.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "NetworkManagerUtils.h"
+#include "libnm-core-public/nm-errors.h"
+#include "libnm-glib-aux/nm-time-utils.h"
+#include "libnm-std-aux/unaligned.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-utils.h"
 
-#include "systemd/nm-sd.h"
+#include "libnm-systemd-core/nm-sd.h"
 
 #define MAX_NEIGHBORS            128
 #define MIN_UPDATE_INTERVAL_NSEC (2 * NM_UTILS_NSEC_PER_SEC)
@@ -28,34 +31,21 @@
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE(NMLldpListener, PROP_NEIGHBORS, );
-
-typedef struct {
+struct _NMLldpListener {
     sd_lldp *   lldp_handle;
     GHashTable *lldp_neighbors;
     GVariant *  variant;
 
+    NMLldpListenerNotify notify_callback;
+    gpointer             notify_user_data;
+
     /* the timestamp in nsec until which we delay updates. */
-    gint64 ratelimit_next_nsec;
-    guint  ratelimit_id;
+    GSource *ratelimit_source;
+    gint64   ratelimit_next_nsec;
 
     int ifindex;
-} NMLldpListenerPrivate;
-
-struct _NMLldpListener {
-    GObject               parent;
-    NMLldpListenerPrivate _priv;
-};
-
-struct _NMLldpListenerClass {
-    GObjectClass parent;
 };
 
-G_DEFINE_TYPE(NMLldpListener, nm_lldp_listener, G_TYPE_OBJECT)
-
-#define NM_LLDP_LISTENER_GET_PRIVATE(self) \
-    _NM_GET_PRIVATE(self, NMLldpListener, NM_IS_LLDP_LISTENER)
-
 /*****************************************************************************/
 
 typedef struct {
@@ -71,27 +61,28 @@ typedef struct {
 
 #define _NMLOG_PREFIX_NAME "lldp"
 #define _NMLOG_DOMAIN      LOGD_DEVICE
-#define _NMLOG(level, ...)                                                                      \
-    G_STMT_START                                                                                \
-    {                                                                                           \
-        const NMLogLevel _level = (level);                                                      \
-                                                                                                \
-        if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) {                                        \
-            char _sbuf[64];                                                                     \
-            int  _ifindex = (self) ? NM_LLDP_LISTENER_GET_PRIVATE(self)->ifindex : 0;           \
-                                                                                                \
-            _nm_log(_level,                                                                     \
-                    _NMLOG_DOMAIN,                                                              \
-                    0,                                                                          \
-                    _ifindex > 0 ? nm_platform_link_get_name(NM_PLATFORM_GET, _ifindex) : NULL, \
-                    NULL,                                                                       \
-                    "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                                \
-                    _NMLOG_PREFIX_NAME,                                                         \
-                    ((_ifindex > 0) ? nm_sprintf_buf(_sbuf, "[%p,%d]", (self), _ifindex)        \
-                                    : ((self) ? nm_sprintf_buf(_sbuf, "[%p]", (self)) : ""))    \
-                        _NM_UTILS_MACRO_REST(__VA_ARGS__));                                     \
-        }                                                                                       \
-    }                                                                                           \
+#define _NMLOG(level, ...)                                                                       \
+    G_STMT_START                                                                                 \
+    {                                                                                            \
+        const NMLogLevel _level = (level);                                                       \
+                                                                                                 \
+        if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) {                                         \
+            char _sbuf[100];                                                                     \
+                                                                                                 \
+            _nm_log(_level,                                                                      \
+                    _NMLOG_DOMAIN,                                                               \
+                    0,                                                                           \
+                    (self) ? nm_platform_link_get_name(NM_PLATFORM_GET, (self)->ifindex) : NULL, \
+                    NULL,                                                                        \
+                    "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                                 \
+                    _NMLOG_PREFIX_NAME,                                                          \
+                    ((self) ? nm_sprintf_buf(_sbuf,                                              \
+                                             "[" NM_HASH_OBFUSCATE_PTR_FMT ",%d]",               \
+                                             NM_HASH_OBFUSCATE_PTR(self),                        \
+                                             (self)->ifindex)                                    \
+                            : "") _NM_UTILS_MACRO_REST(__VA_ARGS__));                            \
+        }                                                                                        \
+    }                                                                                            \
     G_STMT_END
 
 #define LOG_NEIGH_FMT "CHASSIS=%u/%s PORT=%u/%s"
@@ -809,80 +800,73 @@ nmtst_lldp_parse_from_raw(const guint8 *raw_data, gsize raw_len)
 /*****************************************************************************/
 
 static void
-data_changed_notify(NMLldpListener *self, NMLldpListenerPrivate *priv)
+data_changed_notify(NMLldpListener *self)
 {
-    nm_clear_g_variant(&priv->variant);
-    _notify(self, PROP_NEIGHBORS);
+    nm_clear_g_variant(&self->variant);
+
+    self->notify_callback(self, self->notify_user_data);
 }
 
 static gboolean
 data_changed_timeout(gpointer user_data)
 {
-    NMLldpListener *       self = user_data;
-    NMLldpListenerPrivate *priv;
+    NMLldpListener *self = user_data;
 
-    g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), G_SOURCE_REMOVE);
-
-    priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
-
-    priv->ratelimit_id        = 0;
-    priv->ratelimit_next_nsec = nm_utils_get_monotonic_timestamp_nsec() + MIN_UPDATE_INTERVAL_NSEC;
-    data_changed_notify(self, priv);
-    return G_SOURCE_REMOVE;
+    nm_clear_g_source_inst(&self->ratelimit_source);
+    self->ratelimit_next_nsec = nm_utils_get_monotonic_timestamp_nsec() + MIN_UPDATE_INTERVAL_NSEC;
+    data_changed_notify(self);
+    return G_SOURCE_CONTINUE;
 }
 
 static void
 data_changed_schedule(NMLldpListener *self)
 {
-    NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
-    gint64                 now_nsec;
+    gint64 now_nsec;
 
-    if (priv->ratelimit_id != 0)
+    if (self->ratelimit_source)
         return;
 
     now_nsec = nm_utils_get_monotonic_timestamp_nsec();
-    if (now_nsec < priv->ratelimit_next_nsec) {
-        priv->ratelimit_id =
-            g_timeout_add_full(G_PRIORITY_LOW,
-                               NM_UTILS_NSEC_TO_MSEC_CEIL(priv->ratelimit_next_nsec - now_nsec),
-                               data_changed_timeout,
-                               self,
-                               NULL);
-        return;
+    if (now_nsec < self->ratelimit_next_nsec) {
+        self->ratelimit_source = nm_g_timeout_source_new(
+            NM_UTILS_NSEC_TO_MSEC_CEIL(self->ratelimit_next_nsec - now_nsec),
+            G_PRIORITY_LOW,
+            data_changed_timeout,
+            self,
+            NULL);
+    } else {
+        self->ratelimit_source =
+            nm_g_idle_source_new(G_PRIORITY_LOW, data_changed_timeout, self, NULL);
     }
 
-    priv->ratelimit_id = g_idle_add_full(G_PRIORITY_LOW, data_changed_timeout, self, NULL);
+    g_source_attach(self->ratelimit_source, NULL);
 }
 
 static void
 process_lldp_neighbor(NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gboolean remove)
 {
-    NMLldpListenerPrivate *                    priv;
     nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
     LldpNeighbor *                             neigh_old;
 
-    g_return_if_fail(NM_IS_LLDP_LISTENER(self));
+    nm_assert(self);
+    nm_assert(self->lldp_handle);
+    nm_assert(self->lldp_neighbors);
 
-    priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
-
-    g_return_if_fail(priv->lldp_handle);
     g_return_if_fail(neighbor_sd);
 
-    nm_assert(priv->lldp_neighbors);
-
     neigh = lldp_neighbor_new(neighbor_sd);
     if (!neigh) {
         _LOGT("process: failed to parse neighbor");
         return;
     }
 
-    neigh_old = g_hash_table_lookup(priv->lldp_neighbors, neigh);
+    neigh_old = g_hash_table_lookup(self->lldp_neighbors, neigh);
 
     if (remove) {
         if (neigh_old) {
             _LOGT("process: %s neigh: " LOG_NEIGH_FMT, "remove", LOG_NEIGH_ARG(neigh));
 
-            g_hash_table_remove(priv->lldp_neighbors, neigh_old);
+            g_hash_table_remove(self->lldp_neighbors, neigh_old);
             goto handle_changed;
         }
         return;
@@ -893,14 +877,14 @@ process_lldp_neighbor(NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gbool
 
     _LOGD("process: %s neigh: " LOG_NEIGH_FMT, neigh_old ? "update" : "new", LOG_NEIGH_ARG(neigh));
 
-    g_hash_table_add(priv->lldp_neighbors, g_steal_pointer(&neigh));
+    g_hash_table_add(self->lldp_neighbors, g_steal_pointer(&neigh));
 
 handle_changed:
     data_changed_schedule(self);
 }
 
 static void
-lldp_event_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata)
+lldp_event_handler(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata)
 {
     process_lldp_neighbor(
         userdata,
@@ -908,25 +892,57 @@ lldp_event_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void
         !NM_IN_SET(event, SD_LLDP_EVENT_ADDED, SD_LLDP_EVENT_UPDATED, SD_LLDP_EVENT_REFRESHED));
 }
 
-gboolean
-nm_lldp_listener_start(NMLldpListener *self, int ifindex, GError **error)
+/*****************************************************************************/
+
+int
+nm_lldp_listener_get_ifindex(NMLldpListener *self)
 {
-    NMLldpListenerPrivate *priv;
-    int                    ret;
+    g_return_val_if_fail(self, 0);
 
-    g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), FALSE);
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(!error || !*error, FALSE);
+    return self->ifindex;
+}
 
-    priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
+/*****************************************************************************/
 
-    if (priv->lldp_handle) {
-        g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "already running");
-        return FALSE;
+GVariant *
+nm_lldp_listener_get_neighbors(NMLldpListener *self)
+{
+    g_return_val_if_fail(self, FALSE);
+
+    if (G_UNLIKELY(!self->variant)) {
+        gs_free LldpNeighbor **neighbors = NULL;
+        GVariantBuilder        array_builder;
+        guint                  i, n;
+
+        g_variant_builder_init(&array_builder, G_VARIANT_TYPE("aa{sv}"));
+        neighbors = (LldpNeighbor **)
+            nm_utils_hash_keys_to_array(self->lldp_neighbors, lldp_neighbor_id_cmp_p, NULL, &n);
+        for (i = 0; i < n; i++)
+            g_variant_builder_add_value(&array_builder, lldp_neighbor_to_variant(neighbors[i]));
+        self->variant = g_variant_ref_sink(g_variant_builder_end(&array_builder));
     }
 
-    ret = sd_lldp_new(&priv->lldp_handle);
-    if (ret < 0) {
+    return self->variant;
+}
+
+/*****************************************************************************/
+
+NMLldpListener *
+nm_lldp_listener_new(int                  ifindex,
+                     NMLldpListenerNotify notify_callback,
+                     gpointer             notify_user_data,
+                     GError **            error)
+{
+    NMLldpListener *self = NULL;
+    sd_lldp *       lldp_handle;
+    int             r;
+
+    g_return_val_if_fail(ifindex > 0, FALSE);
+    g_return_val_if_fail(!error || !*error, FALSE);
+    g_return_val_if_fail(notify_callback, FALSE);
+
+    r = sd_lldp_new(&lldp_handle);
+    if (r < 0) {
         g_set_error_literal(error,
                             NM_DEVICE_ERROR,
                             NM_DEVICE_ERROR_FAILED,
@@ -934,189 +950,77 @@ nm_lldp_listener_start(NMLldpListener *self, int ifindex, GError **error)
         return FALSE;
     }
 
-    ret = sd_lldp_set_ifindex(priv->lldp_handle, ifindex);
-    if (ret < 0) {
+    r = sd_lldp_set_ifindex(lldp_handle, ifindex);
+    if (r < 0) {
         g_set_error_literal(error,
                             NM_DEVICE_ERROR,
                             NM_DEVICE_ERROR_FAILED,
                             "failed setting ifindex");
-        goto err;
+        goto fail_handle;
     }
 
-    ret = sd_lldp_set_callback(priv->lldp_handle, lldp_event_handler, self);
-    if (ret < 0) {
-        g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "set callback failed");
-        goto err;
-    }
+    r = sd_lldp_set_neighbors_max(lldp_handle, MAX_NEIGHBORS);
+    nm_assert(r == 0);
 
-    ret = sd_lldp_set_neighbors_max(priv->lldp_handle, MAX_NEIGHBORS);
-    nm_assert(ret == 0);
+    self  = g_slice_new(NMLldpListener);
+    *self = (NMLldpListener){
+        .ifindex          = ifindex,
+        .notify_callback  = notify_callback,
+        .notify_user_data = notify_user_data,
+    };
 
-    priv->ifindex = ifindex;
+    r = sd_lldp_set_callback(lldp_handle, lldp_event_handler, self);
+    if (r < 0) {
+        g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "set callback failed");
+        goto fail_handle;
+    }
 
-    ret = sd_lldp_attach_event(priv->lldp_handle, NULL, 0);
-    if (ret < 0) {
+    r = sd_lldp_attach_event(lldp_handle, NULL, 0);
+    if (r < 0) {
         g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "attach event failed");
-        goto err_free;
+        goto fail_attached;
     }
 
-    ret = sd_lldp_start(priv->lldp_handle);
-    if (ret < 0) {
+    r = sd_lldp_start(lldp_handle);
+    if (r < 0) {
         g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "start failed");
-        goto err;
+        goto fail_attached;
     }
 
-    priv->lldp_neighbors = g_hash_table_new_full(lldp_neighbor_id_hash,
+    self->lldp_neighbors = g_hash_table_new_full(lldp_neighbor_id_hash,
                                                  lldp_neighbor_id_equal,
                                                  (GDestroyNotify) lldp_neighbor_free,
                                                  NULL);
-
-    _LOGD("start");
-
-    return TRUE;
-
-err:
-    sd_lldp_detach_event(priv->lldp_handle);
-err_free:
-    sd_lldp_unref(priv->lldp_handle);
-    priv->lldp_handle = NULL;
-    priv->ifindex     = 0;
-    return FALSE;
+    self->lldp_handle    = lldp_handle;
+
+    _LOGD("start lldp listener");
+    return self;
+
+fail_attached:
+    sd_lldp_detach_event(lldp_handle);
+fail_handle:
+    if (self)
+        nm_g_slice_free(self);
+    sd_lldp_unref(lldp_handle);
+    return NULL;
 }
 
 void
-nm_lldp_listener_stop(NMLldpListener *self)
+nm_lldp_listener_destroy(NMLldpListener *self)
 {
-    NMLldpListenerPrivate *priv;
-    guint                  size;
-    gboolean               changed = FALSE;
-
-    g_return_if_fail(NM_IS_LLDP_LISTENER(self));
-    priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
-
-    if (priv->lldp_handle) {
-        _LOGD("stop");
-        sd_lldp_stop(priv->lldp_handle);
-        sd_lldp_detach_event(priv->lldp_handle);
-        sd_lldp_unref(priv->lldp_handle);
-        priv->lldp_handle = NULL;
-
-        size = g_hash_table_size(priv->lldp_neighbors);
-        g_hash_table_remove_all(priv->lldp_neighbors);
-        nm_clear_pointer(&priv->lldp_neighbors, g_hash_table_unref);
-        if (size > 0 || priv->ratelimit_id != 0)
-            changed = TRUE;
-    }
+    g_return_if_fail(self);
 
-    nm_clear_g_source(&priv->ratelimit_id);
-    priv->ratelimit_next_nsec = 0;
-    priv->ifindex             = 0;
+    sd_lldp_stop(self->lldp_handle);
+    sd_lldp_detach_event(self->lldp_handle);
+    sd_lldp_unref(self->lldp_handle);
 
-    if (changed)
-        data_changed_notify(self, priv);
-}
-
-gboolean
-nm_lldp_listener_is_running(NMLldpListener *self)
-{
-    NMLldpListenerPrivate *priv;
-
-    g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), FALSE);
-
-    priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
-    return !!priv->lldp_handle;
-}
-
-GVariant *
-nm_lldp_listener_get_neighbors(NMLldpListener *self)
-{
-    NMLldpListenerPrivate *priv;
-
-    g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), FALSE);
-
-    priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
-
-    if (G_UNLIKELY(!priv->variant)) {
-        gs_free LldpNeighbor **neighbors = NULL;
-        GVariantBuilder        array_builder;
-        guint                  i, n;
-
-        g_variant_builder_init(&array_builder, G_VARIANT_TYPE("aa{sv}"));
-        neighbors = (LldpNeighbor **)
-            nm_utils_hash_keys_to_array(priv->lldp_neighbors, lldp_neighbor_id_cmp_p, NULL, &n);
-        for (i = 0; i < n; i++)
-            g_variant_builder_add_value(&array_builder, lldp_neighbor_to_variant(neighbors[i]));
-        priv->variant = g_variant_ref_sink(g_variant_builder_end(&array_builder));
-    }
-    return priv->variant;
-}
-
-static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMLldpListener *self = NM_LLDP_LISTENER(object);
-
-    switch (prop_id) {
-    case PROP_NEIGHBORS:
-        g_value_set_variant(value, nm_lldp_listener_get_neighbors(self));
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
-nm_lldp_listener_init(NMLldpListener *self)
-{
-    _LOGT("lldp listener created");
-}
+    nm_clear_g_source_inst(&self->ratelimit_source);
 
-NMLldpListener *
-nm_lldp_listener_new(void)
-{
-    return g_object_new(NM_TYPE_LLDP_LISTENER, NULL);
-}
+    g_hash_table_destroy(self->lldp_neighbors);
 
-static void
-dispose(GObject *object)
-{
-    nm_lldp_listener_stop(NM_LLDP_LISTENER(object));
-
-    G_OBJECT_CLASS(nm_lldp_listener_parent_class)->dispose(object);
-}
-
-static void
-finalize(GObject *object)
-{
-    NMLldpListener *       self = NM_LLDP_LISTENER(object);
-    NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
-
-    nm_lldp_listener_stop(self);
-
-    nm_clear_g_variant(&priv->variant);
+    nm_g_variant_unref(self->variant);
 
     _LOGT("lldp listener destroyed");
 
-    G_OBJECT_CLASS(nm_lldp_listener_parent_class)->finalize(object);
-}
-
-static void
-nm_lldp_listener_class_init(NMLldpListenerClass *klass)
-{
-    GObjectClass *object_class = G_OBJECT_CLASS(klass);
-
-    object_class->dispose      = dispose;
-    object_class->finalize     = finalize;
-    object_class->get_property = get_property;
-
-    obj_properties[PROP_NEIGHBORS] =
-        g_param_spec_variant(NM_LLDP_LISTENER_NEIGHBORS,
-                             "",
-                             "",
-                             G_VARIANT_TYPE("aa{sv}"),
-                             NULL,
-                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-    g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+    nm_g_slice_free(self);
 }
diff --git a/src/core/devices/nm-lldp-listener.h b/src/core/devices/nm-lldp-listener.h
index 9d3e2436..762eb3d2 100644
--- a/src/core/devices/nm-lldp-listener.h
+++ b/src/core/devices/nm-lldp-listener.h
@@ -6,28 +6,21 @@
 #ifndef __NM_LLDP_LISTENER__
 #define __NM_LLDP_LISTENER__
 
-#define NM_TYPE_LLDP_LISTENER (nm_lldp_listener_get_type())
-#define NM_LLDP_LISTENER(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LLDP_LISTENER, NMLldpListener))
-#define NM_LLDP_LISTENER_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
-#define NM_IS_LLDP_LISTENER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_LLDP_LISTENER))
-#define NM_IS_LLDP_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_LLDP_LISTENER))
-#define NM_LLDP_LISTENER_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
-
-#define NM_LLDP_LISTENER_NEIGHBORS "neighbors"
-
-typedef struct _NMLldpListenerClass NMLldpListenerClass;
-
-GType           nm_lldp_listener_get_type(void);
-NMLldpListener *nm_lldp_listener_new(void);
-gboolean        nm_lldp_listener_start(NMLldpListener *self, int ifindex, GError **error);
-void            nm_lldp_listener_stop(NMLldpListener *self);
-gboolean        nm_lldp_listener_is_running(NMLldpListener *self);
+/*****************************************************************************/
 
+typedef void (*NMLldpListenerNotify)(NMLldpListener *self, gpointer user_data);
+
+NMLldpListener *nm_lldp_listener_new(int                  ifindex,
+                                     NMLldpListenerNotify notify_callback,
+                                     gpointer             notify_user_data,
+                                     GError **            error);
+void            nm_lldp_listener_destroy(NMLldpListener *self);
+
+int       nm_lldp_listener_get_ifindex(NMLldpListener *self);
 GVariant *nm_lldp_listener_get_neighbors(NMLldpListener *self);
 
+/*****************************************************************************/
+
 GVariant *nmtst_lldp_parse_from_raw(const guint8 *raw_data, gsize raw_len);
 
 #endif /* __NM_LLDP_LISTENER__ */
diff --git a/src/core/devices/ovs/meson.build b/src/core/devices/ovs/meson.build
index 81c29bd6..1974ff4c 100644
--- a/src/core/devices/ovs/meson.build
+++ b/src/core/devices/ovs/meson.build
@@ -13,7 +13,6 @@ libnm_device_plugin_ovs = shared_module(
     core_plugin_dep,
     jansson_dep,
   ],
-  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c
index 3ae8a481..26a5ba00 100644
--- a/src/core/devices/ovs/nm-device-ovs-bridge.c
+++ b/src/core/devices/ovs/nm-device-ovs-bridge.c
@@ -16,7 +16,7 @@
 #include "nm-setting-connection.h"
 #include "nm-setting-ovs-bridge.h"
 #include "nm-setting-ovs-external-ids.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceOvsBridge
 #include "devices/nm-device-logging.h"
@@ -134,9 +134,7 @@ static const NMDBusInterfaceInfoExtended interface_info_device_ovs_bridge = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE,
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Slaves", "ao", NM_DEVICE_SLAVES), ),
-        .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Slaves", "ao", NM_DEVICE_SLAVES), ), ),
 };
 
 static void
diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c
index 5d07c211..46a612ac 100644
--- a/src/core/devices/ovs/nm-device-ovs-interface.c
+++ b/src/core/devices/ovs/nm-device-ovs-interface.c
@@ -78,6 +78,15 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 }
 
 static gboolean
+can_auto_connect(NMDevice *device, NMSettingsConnection *sett_conn, char **specific_object)
+{
+    NMDeviceOvsInterface *       self = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    return nm_ovsdb_is_ready(priv->ovsdb);
+}
+
+static gboolean
 check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
 {
     NMSettingOvsInterface *s_ovs_iface;
@@ -405,10 +414,7 @@ dispose(GObject *object)
 }
 
 static const NMDBusInterfaceInfoExtended interface_info_device_ovs_interface = {
-    .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
-        NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE,
-        .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ), ),
-    .legacy_property_changed = TRUE,
+    .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE, ),
 };
 
 static void
@@ -427,6 +433,7 @@ nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
     device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_OPENVSWITCH);
 
+    device_class->can_auto_connect                    = can_auto_connect;
     device_class->can_update_from_platform_link       = can_update_from_platform_link;
     device_class->deactivate                          = deactivate;
     device_class->deactivate_async                    = deactivate_async;
diff --git a/src/core/devices/ovs/nm-device-ovs-port.c b/src/core/devices/ovs/nm-device-ovs-port.c
index 2ecb95e8..f11c5852 100644
--- a/src/core/devices/ovs/nm-device-ovs-port.c
+++ b/src/core/devices/ovs/nm-device-ovs-port.c
@@ -167,9 +167,7 @@ static const NMDBusInterfaceInfoExtended interface_info_device_ovs_port = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_OVS_PORT,
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Slaves", "ao", NM_DEVICE_SLAVES), ),
-        .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Slaves", "ao", NM_DEVICE_SLAVES), ), ),
 };
 
 static void
diff --git a/src/core/devices/ovs/nm-ovs-factory.c b/src/core/devices/ovs/nm-ovs-factory.c
index e7af38d8..6f5d8832 100644
--- a/src/core/devices/ovs/nm-ovs-factory.c
+++ b/src/core/devices/ovs/nm-ovs-factory.c
@@ -10,8 +10,8 @@
 #include "nm-device-ovs-interface.h"
 #include "nm-device-ovs-port.h"
 #include "nm-device-ovs-bridge.h"
-#include "platform/nm-platform.h"
-#include "nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "settings/nm-settings.h"
 #include "devices/nm-device-factory.h"
 #include "devices/nm-device-private.h"
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index da3a7989..b8d5311a 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -10,10 +10,10 @@
 #include <gmodule.h>
 #include <gio/gunixsocketaddress.h>
 
-#include "nm-glib-aux/nm-jansson.h"
-#include "nm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-jansson.h"
+#include "libnm-glib-aux/nm-str-buf.h"
 #include "nm-core-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "devices/nm-device.h"
 #include "nm-manager.h"
 #include "nm-setting-ovs-external-ids.h"
diff --git a/src/core/devices/team/meson.build b/src/core/devices/team/meson.build
index d0ff4caa..b1ee9103 100644
--- a/src/core/devices/team/meson.build
+++ b/src/core/devices/team/meson.build
@@ -11,7 +11,6 @@ libnm_device_plugin_team = shared_module(
     jansson_dep,
     libteamdctl_dep,
   ],
-  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c
index d2d71729..0f1a8c4c 100644
--- a/src/core/devices/team/nm-device-team.c
+++ b/src/core/devices/team/nm-device-team.c
@@ -15,15 +15,15 @@
 #include <teamdctl.h>
 #include <stdlib.h>
 
-#include "nm-glib-aux/nm-jansson.h"
+#include "libnm-glib-aux/nm-jansson.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-config.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
 #include "nm-ip4-config.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceTeam
 #include "devices/nm-device-logging.h"
@@ -829,12 +829,12 @@ enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gbool
                           "enslaved team port %s config not changed, not connected to teamd",
                           slave_iface);
                 } else {
-                    int   err;
-                    char *sanitized_config;
+                    gs_free char *sanitized_config = NULL;
+                    int           err;
 
-                    sanitized_config = g_strdelimit(g_strdup(config), "\r\n", ' ');
+                    sanitized_config = g_strdup(config);
+                    g_strdelimit(sanitized_config, "\r\n", ' ');
                     err = teamdctl_port_config_update_raw(priv->tdc, slave_iface, sanitized_config);
-                    g_free(sanitized_config);
                     if (err != 0) {
                         _LOGE(LOGD_TEAM,
                               "failed to update config for port %s (err=%d)",
@@ -1042,17 +1042,13 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_team = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_TEAM,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Carrier", "b", NM_DEVICE_CARRIER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Slaves", "ao", NM_DEVICE_SLAVES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Config",
-                                                             "s",
-                                                             NM_DEVICE_TEAM_CONFIG), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Carrier", "b", NM_DEVICE_CARRIER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Slaves", "ao", NM_DEVICE_SLAVES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Config",
+                                                           "s",
+                                                           NM_DEVICE_TEAM_CONFIG), ), ),
 };
 
 static void
diff --git a/src/core/devices/team/nm-team-factory.c b/src/core/devices/team/nm-team-factory.c
index 57b51bf8..e2040d19 100644
--- a/src/core/devices/team/nm-team-factory.c
+++ b/src/core/devices/team/nm-team-factory.c
@@ -10,8 +10,8 @@
 #include "nm-manager.h"
 #include "devices/nm-device-factory.h"
 #include "nm-device-team.h"
-#include "platform/nm-platform.h"
-#include "nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/devices/tests/test-acd.c b/src/core/devices/tests/test-acd.c
index b0af59db..b4b65165 100644
--- a/src/core/devices/tests/test-acd.c
+++ b/src/core/devices/tests/test-acd.c
@@ -78,8 +78,8 @@ fixture_setup(test_fixture *fixture, gconstpointer user_data)
     fixture->ifindex1 =
         nmtstp_link_get_typed(NM_PLATFORM_GET, -1, IFACE_VETH1, NM_LINK_TYPE_VETH)->ifindex;
 
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, fixture->ifindex0, NULL));
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, fixture->ifindex1, NULL));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, fixture->ifindex0, IFF_UP, TRUE) >= 0);
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, fixture->ifindex1, IFF_UP, TRUE) >= 0);
 
     fixture->hwaddr0 =
         nm_platform_link_get_address(NM_PLATFORM_GET, fixture->ifindex0, &fixture->hwaddr0_len);
diff --git a/src/core/devices/tests/test-lldp.c b/src/core/devices/tests/test-lldp.c
index ef0b1549..c64c7c11 100644
--- a/src/core/devices/tests/test-lldp.c
+++ b/src/core/devices/tests/test-lldp.c
@@ -13,7 +13,7 @@
 #include <sys/types.h>
 
 #include "devices/nm-lldp-listener.h"
-#include "systemd/nm-sd.h"
+#include "libnm-systemd-core/nm-sd.h"
 
 #include "platform/tests/test-common.h"
 
@@ -83,6 +83,7 @@ typedef struct {
     const uint8_t *frame;
     const char *   as_variant;
 } TestRecvFrame;
+
 #define TEST_RECV_FRAME_DEFINE(name, _as_variant, ...)        \
     static const guint8        _##name##_v[] = {__VA_ARGS__}; \
     static const TestRecvFrame name          = {              \
@@ -92,11 +93,17 @@ typedef struct {
     }
 
 typedef struct {
+    int        num_called;
+    GMainLoop *loop_to_quit;
+} TestRecvCallbackInfo;
+
+typedef struct {
     guint                expected_num_called;
     gsize                frames_len;
     const TestRecvFrame *frames[10];
-    void (*check)(GMainLoop *loop, NMLldpListener *listener);
+    void (*check)(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info);
 } TestRecvData;
+
 #define TEST_RECV_DATA_DEFINE(name, _expected_num_called, _check, ...) \
     static const TestRecvData name = {                                 \
         .expected_num_called = _expected_num_called,                   \
@@ -213,7 +220,7 @@ _test_recv_data0_check_do(GMainLoop *loop, NMLldpListener *listener, const TestR
 }
 
 static void
-_test_recv_data0_check(GMainLoop *loop, NMLldpListener *listener)
+_test_recv_data0_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info)
 {
     _test_recv_data0_check_do(loop, listener, &_test_recv_data0_frame0);
 }
@@ -528,7 +535,7 @@ TEST_RECV_FRAME_DEFINE(
 );
 
 static void
-_test_recv_data1_check(GMainLoop *loop, NMLldpListener *listener)
+_test_recv_data1_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info)
 {
     GVariant *       neighbors, *attr, *child;
     gs_unref_variant GVariant *neighbor = NULL;
@@ -756,21 +763,17 @@ TEST_RECV_FRAME_DEFINE(
 );
 
 static void
-_test_recv_data2_ttl1_check(GMainLoop *loop, NMLldpListener *listener)
+_test_recv_data2_ttl1_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info)
 {
-    gulong    notify_id;
     GVariant *neighbors;
 
     _test_recv_data0_check_do(loop, listener, &_test_recv_data2_frame0_ttl1);
 
     /* wait for signal. */
-    notify_id = g_signal_connect(listener,
-                                 "notify::" NM_LLDP_LISTENER_NEIGHBORS,
-                                 nmtst_main_loop_quit_on_notify,
-                                 loop);
+    info->loop_to_quit = loop;
     if (!nmtst_main_loop_run(loop, 5000))
         g_assert_not_reached();
-    nm_clear_g_signal_handler(listener, &notify_id);
+    info->loop_to_quit = NULL;
 
     neighbors = nm_lldp_listener_get_neighbors(listener);
     nmtst_assert_variant_is_of_type(neighbors, G_VARIANT_TYPE("aa{sv}"));
@@ -852,46 +855,37 @@ again:
     memcpy(fixture->mac, link->l_address.data, ETH_ALEN);
 }
 
-typedef struct {
-    int num_called;
-} TestRecvCallbackInfo;
-
 static void
-lldp_neighbors_changed(NMLldpListener *lldp_listener, GParamSpec *pspec, gpointer user_data)
+lldp_neighbors_changed(NMLldpListener *lldp_listener, gpointer user_data)
 {
     TestRecvCallbackInfo *info = user_data;
 
     info->num_called++;
+    if (info->loop_to_quit)
+        g_main_loop_quit(info->loop_to_quit);
 }
 
 static void
 test_recv(TestRecvFixture *fixture, gconstpointer user_data)
 {
-    const TestRecvData *data                 = user_data;
-    gs_unref_object NMLldpListener *listener = NULL;
-    GMainLoop *                     loop;
-    TestRecvCallbackInfo            info = {};
-    gsize                           i_frames;
-    gulong                          notify_id;
-    GError *                        error = NULL;
-    guint                           sd_id;
+    const TestRecvData * data = user_data;
+    NMLldpListener *     listener;
+    GMainLoop *          loop;
+    TestRecvCallbackInfo info = {};
+    gsize                i_frames;
+    GError *             error = NULL;
+    guint                sd_id;
 
     if (fixture->ifindex == 0) {
         g_test_skip("Tun device not available");
         return;
     }
 
-    listener = nm_lldp_listener_new();
-    g_assert(listener != NULL);
-    g_assert(nm_lldp_listener_start(listener, fixture->ifindex, &error));
-    g_assert_no_error(error);
+    listener = nm_lldp_listener_new(fixture->ifindex, lldp_neighbors_changed, &info, &error);
+    nmtst_assert_success(listener, error);
 
-    notify_id = g_signal_connect(listener,
-                                 "notify::" NM_LLDP_LISTENER_NEIGHBORS,
-                                 (GCallback) lldp_neighbors_changed,
-                                 &info);
-    loop      = g_main_loop_new(NULL, FALSE);
-    sd_id     = nm_sd_event_attach_default();
+    loop  = g_main_loop_new(NULL, FALSE);
+    sd_id = nm_sd_event_attach_default();
 
     for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
         const TestRecvFrame *f = data->frames[i_frames];
@@ -904,9 +898,9 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data)
 
     g_assert_cmpint(info.num_called, ==, data->expected_num_called);
 
-    nm_clear_g_signal_handler(listener, &notify_id);
+    data->check(loop, listener, &info);
 
-    data->check(loop, listener);
+    nm_clear_pointer(&listener, nm_lldp_listener_destroy);
 
     nm_clear_g_source(&sd_id);
     nm_clear_pointer(&loop, g_main_loop_unref);
diff --git a/src/core/devices/wifi/meson.build b/src/core/devices/wifi/meson.build
index 743937db..85553c53 100644
--- a/src/core/devices/wifi/meson.build
+++ b/src/core/devices/wifi/meson.build
@@ -22,7 +22,6 @@ libnm_device_plugin_wifi_static = static_library(
   dependencies: [
     core_plugin_dep,
   ],
-  c_args: daemon_c_flags,
 )
 
 libnm_device_plugin_wifi_static_dep = declare_dependency(
@@ -38,7 +37,6 @@ libnm_device_plugin_wifi = shared_module(
     core_plugin_dep,
     libnm_device_plugin_wifi_static_dep
   ],
-  c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c
index 95ade44b..56338743 100644
--- a/src/core/devices/wifi/nm-device-iwd.c
+++ b/src/core/devices/wifi/nm-device-iwd.c
@@ -13,16 +13,16 @@
 #include "devices/nm-device.h"
 #include "nm-act-request.h"
 #include "nm-config.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
-#include "nm-glib-aux/nm-ref-string.h"
+#include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-iwd-manager.h"
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-wireless.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 #include "nm-utils.h"
 #include "nm-wifi-common.h"
 #include "nm-wifi-utils.h"
@@ -55,7 +55,7 @@ typedef struct {
     CList                         aps_lst_head;
     NMWifiAP *                    current_ap;
     GCancellable *                cancellable;
-    NMDeviceWifiCapabilities      capabilities;
+    _NMDeviceWifiCapabilities     capabilities;
     NMActRequestGetSecretsCallId *wifi_secrets_id;
     guint                         periodic_scan_id;
     guint                         periodic_update_id;
@@ -265,7 +265,7 @@ ap_from_network(NMDeviceIwd *self,
         .bss_path       = bss_path,
         .last_seen_msec = last_seen_msec,
         .bssid_valid    = TRUE,
-        .mode           = NM_802_11_MODE_INFRA,
+        .mode           = _NM_802_11_MODE_INFRA,
         .rsn_flags      = ap_security_flags_from_network_type(type),
         .ssid           = ssid,
         .signal_percent = nm_wifi_utils_level_to_quality(signal / 100),
@@ -588,10 +588,16 @@ deactivate(NMDevice *device)
             return;
     }
 
-    cleanup_association_attempt(self, TRUE);
+    cleanup_association_attempt(self, FALSE);
     priv->act_mode_switch = FALSE;
 
-    if (!priv->dbus_station_proxy)
+    /* Don't trigger any actions on the IWD side until the device is managed */
+    if (priv->iwd_autoconnect && nm_device_get_state(device) < NM_DEVICE_STATE_DISCONNECTED)
+        return;
+
+    if (priv->dbus_station_proxy)
+        send_disconnect(self);
+    else
         reset_mode(self, NULL, NULL, NULL);
 }
 
@@ -647,6 +653,11 @@ deactivate_async(NMDevice *                 device,
     cleanup_association_attempt(self, FALSE);
     priv->act_mode_switch = FALSE;
 
+    if (priv->iwd_autoconnect && nm_device_get_state(device) < NM_DEVICE_STATE_DISCONNECTED) {
+        nm_utils_invoke_on_idle(cancellable, disconnect_cb_on_idle, user_data);
+        return;
+    }
+
     if (priv->dbus_station_proxy) {
         g_dbus_proxy_call(priv->dbus_station_proxy,
                           "Disconnect",
@@ -801,7 +812,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
         NMSettingWirelessSecurity *s_wireless_sec =
             nm_connection_get_setting_wireless_security(connection);
 
-        if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) {
+        if (!(priv->capabilities & _NM_WIFI_DEVICE_CAP_AP)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
                                        "device does not support Access Point mode");
@@ -819,7 +830,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
         NMSettingWirelessSecurity *s_wireless_sec =
             nm_connection_get_setting_wireless_security(connection);
 
-        if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) {
+        if (!(priv->capabilities & _NM_WIFI_DEVICE_CAP_ADHOC)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
                                        "device does not support Ad-Hoc mode");
@@ -1034,16 +1045,15 @@ complete_connection(NMDevice *           device,
     }
 
     ssid_utf8 = iwd_ssid_to_str(ssid);
-    nm_utils_complete_generic(
-        nm_device_get_platform(device),
-        connection,
-        NM_SETTING_WIRELESS_SETTING_NAME,
-        existing_connections,
-        ssid_utf8,
-        ssid_utf8,
-        NULL,
-        nm_setting_wireless_get_mac_address(s_wifi) ? NULL : nm_device_get_iface(device),
-        TRUE);
+    nm_utils_complete_generic(nm_device_get_platform(device),
+                              connection,
+                              NM_SETTING_WIRELESS_SETTING_NAME,
+                              existing_connections,
+                              ssid_utf8,
+                              ssid_utf8,
+                              NULL,
+                              NULL,
+                              TRUE);
 
     if (hidden)
         g_object_set(s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
@@ -1306,6 +1316,7 @@ static gboolean
 try_reply_agent_request(NMDeviceIwd *          self,
                         NMConnection *         connection,
                         GDBusMethodInvocation *invocation,
+                        gboolean               allow_existing,
                         const char **          setting_name,
                         const char **          setting_key,
                         gboolean *             replied)
@@ -1320,56 +1331,64 @@ try_reply_agent_request(NMDeviceIwd *          self,
     *replied = FALSE;
 
     if (nm_streq(method_name, "RequestPassphrase")) {
-        const char *psk;
-
         if (!s_wireless_sec)
             return FALSE;
 
-        psk = nm_setting_wireless_security_get_psk(s_wireless_sec);
-        if (psk) {
-            _LOGD(LOGD_DEVICE | LOGD_WIFI, "Returning the PSK to the IWD Agent");
+        if (allow_existing) {
+            const char *psk = nm_setting_wireless_security_get_psk(s_wireless_sec);
 
-            g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", psk));
-            *replied = TRUE;
-            return TRUE;
+            if (psk) {
+                _LOGD(LOGD_DEVICE | LOGD_WIFI, "Returning the PSK to the IWD Agent");
+
+                g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", psk));
+                *replied = TRUE;
+                return TRUE;
+            }
         }
 
         *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
         *setting_key  = NM_SETTING_WIRELESS_SECURITY_PSK;
         return TRUE;
     } else if (nm_streq(method_name, "RequestPrivateKeyPassphrase")) {
-        const char *password;
-
         if (!s_8021x)
             return FALSE;
 
-        password = nm_setting_802_1x_get_private_key_password(s_8021x);
-        if (password) {
-            _LOGD(LOGD_DEVICE | LOGD_WIFI, "Returning the private key password to the IWD Agent");
+        if (allow_existing) {
+            const char *password = nm_setting_802_1x_get_private_key_password(s_8021x);
 
-            g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", password));
-            *replied = TRUE;
-            return TRUE;
+            if (password) {
+                _LOGD(LOGD_DEVICE | LOGD_WIFI,
+                      "Returning the private key password to the IWD Agent");
+
+                g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", password));
+                *replied = TRUE;
+                return TRUE;
+            }
         }
 
         *setting_name = NM_SETTING_802_1X_SETTING_NAME;
         *setting_key  = NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD;
         return TRUE;
     } else if (nm_streq(method_name, "RequestUserNameAndPassword")) {
-        const char *identity, *password;
+        const char *identity;
 
         if (!s_8021x)
             return FALSE;
 
         identity = nm_setting_802_1x_get_identity(s_8021x);
-        password = nm_setting_802_1x_get_password(s_8021x);
-        if (identity && password) {
-            _LOGD(LOGD_DEVICE | LOGD_WIFI, "Returning the username and password to the IWD Agent");
 
-            g_dbus_method_invocation_return_value(invocation,
-                                                  g_variant_new("(ss)", identity, password));
-            *replied = TRUE;
-            return TRUE;
+        if (allow_existing) {
+            const char *password = nm_setting_802_1x_get_password(s_8021x);
+
+            if (identity && password) {
+                _LOGD(LOGD_DEVICE | LOGD_WIFI,
+                      "Returning the username and password to the IWD Agent");
+
+                g_dbus_method_invocation_return_value(invocation,
+                                                      g_variant_new("(ss)", identity, password));
+                *replied = TRUE;
+                return TRUE;
+            }
         }
 
         *setting_name = NM_SETTING_802_1X_SETTING_NAME;
@@ -1379,18 +1398,19 @@ try_reply_agent_request(NMDeviceIwd *          self,
             *setting_key = NM_SETTING_802_1X_PASSWORD;
         return TRUE;
     } else if (nm_streq(method_name, "RequestUserPassword")) {
-        const char *password;
-
         if (!s_8021x)
             return FALSE;
 
-        password = nm_setting_802_1x_get_password(s_8021x);
-        if (password) {
-            _LOGD(LOGD_DEVICE | LOGD_WIFI, "Returning the user password to the IWD Agent");
+        if (allow_existing) {
+            const char *password = nm_setting_802_1x_get_password(s_8021x);
 
-            g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", password));
-            *replied = TRUE;
-            return TRUE;
+            if (password) {
+                _LOGD(LOGD_DEVICE | LOGD_WIFI, "Returning the user password to the IWD Agent");
+
+                g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", password));
+                *replied = TRUE;
+                return TRUE;
+            }
         }
 
         *setting_name = NM_SETTING_802_1X_SETTING_NAME;
@@ -1441,6 +1461,8 @@ wifi_secrets_cb(NMActRequest *                req,
     gboolean                     replied;
     NMSecretAgentGetSecretsFlags get_secret_flags =
         NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+    NMIwdNetworkSecurity security;
+    NMConnection *       connection;
 
     nm_utils_user_data_unpack(user_data, &self, &invocation);
 
@@ -1473,9 +1495,18 @@ wifi_secrets_cb(NMActRequest *                req,
         goto secrets_error;
     }
 
+    connection = nm_device_get_applied_connection(device);
+
+    if (nm_wifi_connection_get_iwd_ssid_and_security(connection, NULL, &security)
+        && security == NM_IWD_NETWORK_SECURITY_PSK) {
+        if (nm_settings_connection_get_timestamp(nm_device_get_settings_connection(device), NULL))
+            get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
+    }
+
     if (!try_reply_agent_request(self,
-                                 nm_act_request_get_applied_connection(req),
+                                 connection,
                                  invocation,
+                                 TRUE,
                                  &setting_name,
                                  &setting_key,
                                  &replied))
@@ -1501,9 +1532,6 @@ wifi_secrets_cb(NMActRequest *                req,
         return;
     }
 
-    if (nm_settings_connection_get_timestamp(nm_act_request_get_settings_connection(req), NULL))
-        get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
-
     /* Request further secrets if we still need something */
     wifi_secrets_get_one(self, setting_name, get_secret_flags, setting_key, invocation);
     return;
@@ -1595,8 +1623,6 @@ network_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data)
             dbus_error = g_dbus_error_get_remote_error(error);
 
         if (nm_streq0(dbus_error, "net.connman.iwd.Failed")) {
-            nm_connection_clear_secrets(connection);
-
             /* 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") && priv->secrets_failed) {
@@ -2610,9 +2636,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     switch (prop_id) {
     case PROP_MODE:
         if (!priv->current_ap)
-            g_value_set_uint(value, NM_802_11_MODE_UNKNOWN);
+            g_value_set_uint(value, _NM_802_11_MODE_UNKNOWN);
         else if (nm_wifi_ap_is_hotspot(priv->current_ap))
-            g_value_set_uint(value, NM_802_11_MODE_AP);
+            g_value_set_uint(value, _NM_802_11_MODE_AP);
         else
             g_value_set_uint(value, nm_wifi_ap_get_mode(priv->current_ap));
 
@@ -3034,7 +3060,7 @@ nm_device_iwd_set_dbus_object(NMDeviceIwd *self, GDBusObject *object)
     GVariantIter *              iter;
     const char *                mode;
     gboolean                    powered;
-    NMDeviceWifiCapabilities    capabilities;
+    _NMDeviceWifiCapabilities   capabilities;
 
     if (!nm_g_object_ref_set(&priv->dbus_obj, object))
         return;
@@ -3096,14 +3122,14 @@ nm_device_iwd_set_dbus_object(NMDeviceIwd *self, GDBusObject *object)
         goto error;
     }
 
-    capabilities = NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN;
+    capabilities = _NM_WIFI_DEVICE_CAP_CIPHER_CCMP | _NM_WIFI_DEVICE_CAP_RSN;
 
     g_variant_get(value, "as", &iter);
     while (g_variant_iter_next(iter, "&s", &mode)) {
         if (nm_streq(mode, "ap"))
-            capabilities |= NM_WIFI_DEVICE_CAP_AP;
+            capabilities |= _NM_WIFI_DEVICE_CAP_AP;
         else if (nm_streq(mode, "ad-hoc"))
-            capabilities |= NM_WIFI_DEVICE_CAP_ADHOC;
+            capabilities |= _NM_WIFI_DEVICE_CAP_ADHOC;
     }
     g_variant_iter_free(iter);
 
@@ -3145,8 +3171,11 @@ nm_device_iwd_agent_query(NMDeviceIwd *self, GDBusMethodInvocation *invocation)
     const char *                 setting_key;
     gboolean                     replied;
     NMWifiAP *                   ap;
+    gboolean                     allow_existing = FALSE;
     NMSecretAgentGetSecretsFlags get_secret_flags =
         NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+    NMIwdNetworkSecurity security;
+    NMConnection *       connection;
     nm_auto_ref_string NMRefString *network_path = NULL;
 
     if (!invocation) {
@@ -3250,9 +3279,37 @@ nm_device_iwd_agent_query(NMDeviceIwd *self, GDBusMethodInvocation *invocation)
         /* Otherwise handle as usual */
     }
 
+    /* Normally for PSK networks require new secret every time IWD asks for
+     * it.  IWD only queries us if it has not saved the PSK (e.g. by policy)
+     * or a previous attempt has failed with current secrets so it wants a
+     * fresh value.  It doesn't know about agent-owned secrets so whenever
+     * possible and the PSK is saved and not asked from NM.  However if this
+     * is a new connection it may include all of the needed settings already
+     * so allow using these, too.  Connection timestamp is set after
+     * activation or after first activation failure (to 0).
+     *
+     * For 802.1x, since IWD assumes the network is pre-provisioned by an
+     * admin and tested, there's no reason for IWD to save secrets in
+     * the network config file and there's no reason to ask for a new value
+     * of a saved (i.e. system-owned) secret because it can't be wrong.
+     * Since NM has a richer set of secret storage options we never specify
+     * NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW and let
+     * nm_settings_connection_get_secrets decide.
+     */
+    connection = nm_device_get_applied_connection(device);
+
+    if (nm_wifi_connection_get_iwd_ssid_and_security(connection, NULL, &security)
+        && security == NM_IWD_NETWORK_SECURITY_PSK) {
+        if (nm_settings_connection_get_timestamp(nm_device_get_settings_connection(device), NULL))
+            get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
+        else
+            allow_existing = TRUE;
+    }
+
     if (!try_reply_agent_request(self,
-                                 nm_device_get_applied_connection(device),
+                                 connection,
                                  invocation,
+                                 allow_existing,
                                  &setting_name,
                                  &setting_key,
                                  &replied)) {
@@ -3263,17 +3320,6 @@ nm_device_iwd_agent_query(NMDeviceIwd *self, GDBusMethodInvocation *invocation)
     if (replied)
         return TRUE;
 
-    /* Normally require new secrets every time IWD asks for them.
-     * IWD only queries us if it has not saved the secrets (e.g. by policy)
-     * or a previous attempt has failed with current secrets so it wants
-     * a fresh set.  However if this is a new connection it may include
-     * all of the needed settings already so allow using these, too.
-     * Connection timestamp is set after activation or after first
-     * activation failure (to 0).
-     */
-    if (nm_settings_connection_get_timestamp(nm_device_get_settings_connection(device), NULL))
-        get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
-
     nm_device_state_changed(device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NO_SECRETS);
     wifi_secrets_get_one(self, setting_name, get_secret_flags, setting_key, invocation);
 
@@ -3452,9 +3498,9 @@ nm_device_iwd_class_init(NMDeviceIwdClass *klass)
     obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_IWD_MODE,
                                                   "",
                                                   "",
-                                                  NM_802_11_MODE_UNKNOWN,
-                                                  NM_802_11_MODE_AP,
-                                                  NM_802_11_MODE_INFRA,
+                                                  _NM_802_11_MODE_UNKNOWN,
+                                                  _NM_802_11_MODE_AP,
+                                                  _NM_802_11_MODE_INFRA,
                                                   G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
     obj_properties[PROP_BITRATE] = g_param_spec_uint(NM_DEVICE_IWD_BITRATE,
@@ -3485,7 +3531,7 @@ nm_device_iwd_class_init(NMDeviceIwdClass *klass)
                           "",
                           0,
                           G_MAXUINT32,
-                          NM_WIFI_DEVICE_CAP_NONE,
+                          _NM_WIFI_DEVICE_CAP_NONE,
                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
     obj_properties[PROP_SCANNING] = g_param_spec_boolean(NM_DEVICE_IWD_SCANNING,
diff --git a/src/core/devices/wifi/nm-device-olpc-mesh.c b/src/core/devices/wifi/nm-device-olpc-mesh.c
index af83c4a3..040350d5 100644
--- a/src/core/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/core/devices/wifi/nm-device-olpc-mesh.c
@@ -28,7 +28,7 @@
 #include "nm-setting-connection.h"
 #include "nm-setting-olpc-mesh.h"
 #include "nm-manager.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceOlpcMesh
 #include "devices/nm-device-logging.h"
@@ -122,6 +122,17 @@ complete_connection(NMDevice *           device,
 
 /*****************************************************************************/
 
+static const char *
+get_dhcp_anycast_address(NMDevice *device)
+{
+    NMSettingOlpcMesh *s_mesh;
+
+    s_mesh = nm_device_get_applied_setting(device, NM_TYPE_SETTING_OLPC_MESH);
+    return s_mesh ? nm_setting_olpc_mesh_get_dhcp_anycast_address(s_mesh) : NULL;
+}
+
+/*****************************************************************************/
+
 static NMActStageReturn
 act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
@@ -178,7 +189,6 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(device);
     NMSettingOlpcMesh *s_mesh;
     GBytes *           ssid;
-    const char *       anycast_addr;
     gboolean           success;
 
     s_mesh = nm_device_get_applied_setting(device, NM_TYPE_SETTING_OLPC_MESH);
@@ -197,9 +207,6 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
         return NM_ACT_STAGE_RETURN_FAILURE;
     }
 
-    anycast_addr = nm_setting_olpc_mesh_get_dhcp_anycast_address(s_mesh);
-    nm_device_set_dhcp_anycast_address(device, anycast_addr);
-
     if (!_mesh_set_channel(self, nm_setting_olpc_mesh_get_channel(s_mesh))) {
         _LOGW(LOGD_WIFI, "Unable to set the mesh channel");
         return NM_ACT_STAGE_RETURN_FAILURE;
@@ -492,19 +499,15 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_olpc_mesh = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_OLPC_MESH,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Companion",
-                                                             "o",
-                                                             NM_DEVICE_OLPC_MESH_COMPANION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Companion",
+                                                           "o",
+                                                           NM_DEVICE_OLPC_MESH_COMPANION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(
                 "ActiveChannel",
                 "u",
                 NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL), ), ),
-    .legacy_property_changed = TRUE,
 };
 
 static void
@@ -531,6 +534,7 @@ nm_device_olpc_mesh_class_init(NMDeviceOlpcMeshClass *klass)
     device_class->act_stage2_config           = act_stage2_config;
     device_class->state_changed               = state_changed;
     device_class->get_dhcp_timeout_for_device = get_dhcp_timeout_for_device;
+    device_class->get_dhcp_anycast_address    = get_dhcp_anycast_address;
 
     obj_properties[PROP_COMPANION] = g_param_spec_string(NM_DEVICE_OLPC_MESH_COMPANION,
                                                          "",
diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c
index fb987600..a8ea2ed7 100644
--- a/src/core/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/core/devices/wifi/nm-device-wifi-p2p.c
@@ -15,16 +15,16 @@
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
 #include "nm-act-request.h"
-#include "nm-core-internal.h"
-#include "nm-glib-aux/nm-ref-string.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-ip4-config.h"
 #include "nm-manager.h"
 #include "nm-manager.h"
 #include "nm-setting-wifi-p2p.h"
 #include "nm-utils.h"
 #include "nm-wifi-p2p-peer.h"
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
 #include "settings/nm-settings.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceWifiP2P
@@ -1149,7 +1149,6 @@ static const NMDBusInterfaceInfoExtended interface_info_device_wifi_p2p = {
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Peers",
                                                            "ao",
                                                            NM_DEVICE_WIFI_P2P_PEERS), ), ),
-    .legacy_property_changed = FALSE,
 };
 
 /*****************************************************************************/
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index 042d4887..fca2fde5 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -12,11 +12,11 @@
 #include <unistd.h>
 #include <linux/if_ether.h>
 
-#include "nm-glib-aux/nm-ref-string.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-ref-string.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-device-wifi-p2p.h"
 #include "nm-wifi-ap.h"
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-private.h"
 #include "nm-dbus-manager.h"
@@ -33,13 +33,13 @@
 #include "nm-setting-ip4-config.h"
 #include "nm-ip4-config.h"
 #include "nm-setting-ip6-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-auth-utils.h"
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
 #include "nm-wifi-utils.h"
 #include "nm-wifi-common.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-config.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceWifi
@@ -114,11 +114,11 @@ typedef struct {
     guint wps_timeout_id;
     guint sup_timeout_id; /* supplicant association timeout */
 
-    NMDeviceWifiCapabilities    capabilities;
-    NMSettingWirelessWakeOnWLan wowlan_restore;
+    _NMDeviceWifiCapabilities    capabilities;
+    _NMSettingWirelessWakeOnWLan wowlan_restore;
 
     NMDeviceWifiP2P *p2p_device;
-    NM80211Mode      mode;
+    _NM80211Mode     mode;
 
     guint32 failed_iface_count;
     gint32  hw_addr_scan_expire;
@@ -487,7 +487,7 @@ _scan_notify_allowed(NMDeviceWifi *self, NMTernary do_kickoff)
 
     if (!c_list_is_empty(&priv->scanning_prohibited_lst_head)) {
         /* something prohibits scanning. */
-    } else if (NM_IN_SET(priv->mode, NM_802_11_MODE_ADHOC, NM_802_11_MODE_AP)) {
+    } else if (NM_IN_SET(priv->mode, _NM_802_11_MODE_ADHOC, _NM_802_11_MODE_AP)) {
         /* Don't scan when a an AP or Ad-Hoc connection is active as it will
          * disrupt connected clients or peers. */
     } else if (NM_IN_SET(state, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_FAILED)) {
@@ -678,7 +678,7 @@ update_seen_bssids_cache(NMDeviceWifi *self, NMWifiAP *ap)
         return;
 
     /* Don't cache the BSSID for Ad-Hoc APs */
-    if (nm_wifi_ap_get_mode(ap) != NM_802_11_MODE_INFRA)
+    if (nm_wifi_ap_get_mode(ap) != _NM_802_11_MODE_INFRA)
         return;
 
     if (nm_device_get_state(NM_DEVICE(self)) == NM_DEVICE_STATE_ACTIVATED
@@ -712,10 +712,11 @@ set_current_ap(NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available_
         priv->current_ap = NULL;
 
     if (old_ap) {
-        NM80211Mode mode = nm_wifi_ap_get_mode(old_ap);
+        _NM80211Mode mode = nm_wifi_ap_get_mode(old_ap);
 
         /* Remove any AP from the internal list if it was created by NM or isn't known to the supplicant */
-        if (NM_IN_SET(mode, NM_802_11_MODE_ADHOC, NM_802_11_MODE_AP) || nm_wifi_ap_get_fake(old_ap))
+        if (NM_IN_SET(mode, _NM_802_11_MODE_ADHOC, _NM_802_11_MODE_AP)
+            || nm_wifi_ap_get_fake(old_ap))
             ap_add_remove(self, FALSE, old_ap, recheck_available_connections);
         g_object_unref(old_ap);
     }
@@ -750,7 +751,7 @@ periodic_update(NMDeviceWifi *self)
         return;
     }
 
-    if (priv->mode == NM_802_11_MODE_AP) {
+    if (priv->mode == _NM_802_11_MODE_AP) {
         /* In AP mode we currently have nothing to do. */
         return;
     }
@@ -845,14 +846,14 @@ remove_all_aps(NMDeviceWifi *self)
 static gboolean
 wake_on_wlan_restore(NMDeviceWifi *self)
 {
-    NMDeviceWifiPrivate *       priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
-    NMSettingWirelessWakeOnWLan w;
+    NMDeviceWifiPrivate *        priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
+    _NMSettingWirelessWakeOnWLan w;
 
     w = priv->wowlan_restore;
-    if (w == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE)
+    if (w == _NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE)
         return TRUE;
 
-    priv->wowlan_restore = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
+    priv->wowlan_restore = _NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
     return nm_platform_wifi_set_wake_on_wlan(NM_PLATFORM_GET,
                                              nm_device_get_ifindex(NM_DEVICE(self)),
                                              w);
@@ -937,14 +938,14 @@ deactivate(NMDevice *device)
      * (usually older ones) don't scan well in adhoc mode.
      */
     if (nm_platform_wifi_get_mode(nm_device_get_platform(device), ifindex)
-        != NM_802_11_MODE_INFRA) {
+        != _NM_802_11_MODE_INFRA) {
         nm_device_take_down(NM_DEVICE(self), TRUE);
-        nm_platform_wifi_set_mode(nm_device_get_platform(device), ifindex, NM_802_11_MODE_INFRA);
+        nm_platform_wifi_set_mode(nm_device_get_platform(device), ifindex, _NM_802_11_MODE_INFRA);
         nm_device_bring_up(NM_DEVICE(self), TRUE, NULL);
     }
 
-    if (priv->mode != NM_802_11_MODE_INFRA) {
-        priv->mode = NM_802_11_MODE_INFRA;
+    if (priv->mode != _NM_802_11_MODE_INFRA) {
+        priv->mode = _NM_802_11_MODE_INFRA;
         _notify(self, PROP_MODE);
     }
 
@@ -1010,14 +1011,14 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
     /* Early exit if supplicant or device doesn't support requested mode */
     mode = nm_setting_wireless_get_mode(s_wireless);
     if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) {
-        if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) {
+        if (!(priv->capabilities & _NM_WIFI_DEVICE_CAP_ADHOC)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
                                        "the device does not support Ad-Hoc networks");
             return FALSE;
         }
     } else if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_AP) == 0) {
-        if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) {
+        if (!(priv->capabilities & _NM_WIFI_DEVICE_CAP_AP)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
                                        "the device does not support Access Point mode");
@@ -1034,7 +1035,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
             }
         }
     } else if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_MESH) == 0) {
-        if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_MESH)) {
+        if (!(priv->capabilities & _NM_WIFI_DEVICE_CAP_MESH)) {
             nm_utils_error_set_literal(error,
                                        NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
                                        "the device does not support Mesh mode");
@@ -1381,8 +1382,8 @@ _hw_addr_set_scanning(NMDeviceWifi *self, gboolean do_reset)
     now = nm_utils_get_monotonic_timestamp_sec();
 
     if (now >= priv->hw_addr_scan_expire) {
-        gs_free char *generate_mac_address_mask = NULL;
-        gs_free char *hw_addr_scan              = NULL;
+        gs_free char *hw_addr_scan = NULL;
+        const char *  generate_mac_address_mask;
 
         /* the random MAC address for scanning expires after a while.
          *
@@ -1433,7 +1434,7 @@ ssids_options_to_ptrarray(GVariant *value, GError **error)
 
             v     = g_variant_get_child_value(value, i);
             bytes = g_variant_get_fixed_array(v, &len, sizeof(guint8));
-            if (len > 32) {
+            if (len > NM_IW_ESSID_MAX_SIZE) {
                 g_set_error(error,
                             NM_DEVICE_ERROR,
                             NM_DEVICE_ERROR_INVALID_ARGUMENT,
@@ -1822,7 +1823,7 @@ _scan_kickoff(NMDeviceWifi *self)
 
             strv = g_new(char *, ssids->len + 1u);
             for (i = 0; i < ssids->len; i++)
-                strv[i] = _nm_utils_ssid_to_string(ssids->pdata[i]);
+                strv[i] = _nm_utils_ssid_to_string_gbytes(ssids->pdata[i]);
             strv[i] = NULL;
 
             nm_assert(ssids->len > 0);
@@ -1884,13 +1885,13 @@ ap_list_dump(gpointer user_data)
 
         _LOGD(LOGD_WIFI_SCAN,
               "APs: [now:%u.%03u, last:%s]",
-              (guint)(now_msec / NM_UTILS_MSEC_PER_SEC),
-              (guint)(now_msec % NM_UTILS_MSEC_PER_SEC),
+              (guint) (now_msec / NM_UTILS_MSEC_PER_SEC),
+              (guint) (now_msec % NM_UTILS_MSEC_PER_SEC),
               priv->scan_last_complete_msec > 0
                   ? nm_sprintf_buf(str_buf,
                                    "%u.%03u",
-                                   (guint)(priv->scan_last_complete_msec / NM_UTILS_MSEC_PER_SEC),
-                                   (guint)(priv->scan_last_complete_msec % NM_UTILS_MSEC_PER_SEC))
+                                   (guint) (priv->scan_last_complete_msec / NM_UTILS_MSEC_PER_SEC),
+                                   (guint) (priv->scan_last_complete_msec % NM_UTILS_MSEC_PER_SEC))
                   : "-1");
         c_list_for_each_entry (ap, &priv->aps_lst_head, aps_lst)
             _ap_dump(self, LOGL_DEBUG, ap, "dump", now_msec);
@@ -1986,19 +1987,19 @@ supplicant_iface_bss_changed_cb(NMSupplicantInterface *iface,
 
         /* Let the manager try to fill in the SSID from seen-bssids lists */
         ssid = nm_wifi_ap_get_ssid(ap);
-        if (!ssid || _nm_utils_is_empty_ssid(ssid)) {
+        if (!ssid || _nm_utils_is_empty_ssid_gbytes(ssid)) {
             /* Try to fill the SSID from the AP database */
             try_fill_ssid_for_hidden_ap(self, ap);
 
             ssid = nm_wifi_ap_get_ssid(ap);
-            if (ssid && !_nm_utils_is_empty_ssid(ssid)) {
+            if (ssid && !_nm_utils_is_empty_ssid_gbytes(ssid)) {
                 gs_free char *s = NULL;
 
                 /* Yay, matched it, no longer treat as hidden */
                 _LOGD(LOGD_WIFI,
                       "matched hidden AP %s => %s",
                       nm_wifi_ap_get_address(ap),
-                      (s = _nm_utils_ssid_to_string(ssid)));
+                      (s = _nm_utils_ssid_to_string_gbytes(ssid)));
             } else {
                 /* Didn't have an entry for this AP in the database */
                 _LOGD(LOGD_WIFI, "failed to match hidden AP %s", nm_wifi_ap_get_address(ap));
@@ -2481,9 +2482,9 @@ supplicant_iface_state(NMDeviceWifi *             self,
 
             _LOGI(LOGD_DEVICE | LOGD_WIFI,
                   "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. %s %s",
-                  priv->mode == NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot"
-                                                  : "Connected to wireless network",
-                  (ssid_str = _nm_utils_ssid_to_string(ssid)));
+                  priv->mode == _NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot"
+                                                   : "Connected to wireless network",
+                  (ssid_str = _nm_utils_ssid_to_string_gbytes(ssid)));
             nm_device_activate_schedule_stage3_ip_config_start(device);
         } else if (devstate == NM_DEVICE_STATE_ACTIVATED)
             periodic_update(self);
@@ -2565,6 +2566,7 @@ supplicant_iface_notify_current_bss(NMSupplicantInterface *iface,
     NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
     NMRefString *        current_bss;
     NMWifiAP *           new_ap = NULL;
+    NMActRequest *       req;
 
     current_bss = nm_supplicant_interface_get_current_bss(iface);
     if (current_bss)
@@ -2598,9 +2600,9 @@ supplicant_iface_notify_current_bss(NMSupplicantInterface *iface,
         _LOGD(LOGD_WIFI,
               "roamed from BSSID %s (%s) to %s (%s)",
               old_bssid ?: "(none)",
-              (old_ssid_s = _nm_utils_ssid_to_string(old_ssid)),
+              (old_ssid_s = _nm_utils_ssid_to_string_gbytes(old_ssid)),
               new_bssid ?: "(none)",
-              (new_ssid_s = _nm_utils_ssid_to_string(new_ssid)));
+              (new_ssid_s = _nm_utils_ssid_to_string_gbytes(new_ssid)));
 
         if (new_bssid) {
             /* The new AP could be in a different layer 3 network
@@ -2612,6 +2614,13 @@ supplicant_iface_notify_current_bss(NMSupplicantInterface *iface,
         }
 
         set_current_ap(self, new_ap, TRUE);
+
+        req = nm_device_get_act_request(NM_DEVICE(self));
+        if (req) {
+            nm_active_connection_set_specific_object(
+                NM_ACTIVE_CONNECTION(req),
+                new_ap ? nm_dbus_object_get_path(NM_DBUS_OBJECT(new_ap)) : NULL);
+        }
     }
 }
 
@@ -2782,21 +2791,21 @@ supplicant_connection_timeout_cb(gpointer user_data)
     connection = nm_act_request_get_applied_connection(req);
     g_assert(connection);
 
-    if (NM_IN_SET(priv->mode, NM_802_11_MODE_ADHOC, NM_802_11_MODE_MESH, NM_802_11_MODE_AP)) {
+    if (NM_IN_SET(priv->mode, _NM_802_11_MODE_ADHOC, _NM_802_11_MODE_MESH, _NM_802_11_MODE_AP)) {
         /* In Ad-Hoc and AP modes there's nothing to check the encryption key
          * (if any), so supplicant timeouts here are almost certainly the wifi
          * driver being really stupid.
          */
         _LOGW(LOGD_DEVICE | LOGD_WIFI,
               "Activation: (wifi) %s network creation took too long, failing activation",
-              priv->mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc" : "Hotspot");
+              priv->mode == _NM_802_11_MODE_ADHOC ? "Ad-Hoc" : "Hotspot");
         nm_device_state_changed(device,
                                 NM_DEVICE_STATE_FAILED,
                                 NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT);
         return FALSE;
     }
 
-    g_assert(priv->mode == NM_802_11_MODE_INFRA);
+    g_assert(priv->mode == _NM_802_11_MODE_INFRA);
 
     if (priv->ssid_found && nm_connection_get_setting_wireless_security(connection)) {
         guint64  timestamp   = 0;
@@ -2948,9 +2957,10 @@ error:
 static gboolean
 wake_on_wlan_enable(NMDeviceWifi *self)
 {
-    NMDeviceWifiPrivate *       priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
-    NMSettingWirelessWakeOnWLan wowl;
-    NMSettingWireless *         s_wireless;
+    NMDeviceWifiPrivate *        priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
+    NMSettingWirelessWakeOnWLan  wowl;
+    _NMSettingWirelessWakeOnWLan wowl2;
+    NMSettingWireless *          s_wireless;
 
     s_wireless = nm_device_get_applied_setting(NM_DEVICE(self), NM_TYPE_SETTING_WIRELESS);
     if (s_wireless) {
@@ -2982,9 +2992,11 @@ wake_on_wlan_enable(NMDeviceWifi *self)
         goto found;
 
     wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
+
 found:
-    if (wowl == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE) {
-        priv->wowlan_restore = wowl;
+    wowl2 = _NM_SETTING_WIRELESS_WAKE_ON_WLAN_CAST(wowl);
+    if (wowl2 == _NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE) {
+        priv->wowlan_restore = wowl2;
         return TRUE;
     }
 
@@ -2993,7 +3005,7 @@ found:
 
     return nm_platform_wifi_set_wake_on_wlan(NM_PLATFORM_GET,
                                              nm_device_get_ifindex(NM_DEVICE(self)),
-                                             wowl);
+                                             wowl2);
 }
 
 static NMActStageReturn
@@ -3022,16 +3034,16 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
     mode = nm_setting_wireless_get_mode(s_wireless);
     if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0)
-        priv->mode = NM_802_11_MODE_INFRA;
+        priv->mode = _NM_802_11_MODE_INFRA;
     else if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0)
-        priv->mode = NM_802_11_MODE_ADHOC;
+        priv->mode = _NM_802_11_MODE_ADHOC;
     else if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_AP) == 0) {
-        priv->mode = NM_802_11_MODE_AP;
+        priv->mode = _NM_802_11_MODE_AP;
 
         /* Scanning not done in AP mode; clear the scan list */
         remove_all_aps(self);
     } else if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_MESH) == 0)
-        priv->mode = NM_802_11_MODE_MESH;
+        priv->mode = _NM_802_11_MODE_MESH;
     _notify(self, PROP_MODE);
 
     /* expire the temporary MAC address used during scanning */
@@ -3044,7 +3056,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     }
 
     /* AP and Mesh modes never use a specific object or existing scanned AP */
-    if (!NM_IN_SET(priv->mode, NM_802_11_MODE_AP, NM_802_11_MODE_MESH)) {
+    if (!NM_IN_SET(priv->mode, _NM_802_11_MODE_AP, _NM_802_11_MODE_MESH)) {
         ap_path = nm_active_connection_get_specific_object(NM_ACTIVE_CONNECTION(req));
         ap      = ap_path ? nm_wifi_ap_lookup_for_device(NM_DEVICE(self), ap_path) : NULL;
     }
@@ -3146,7 +3158,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     NMDeviceWifi *       self                  = NM_DEVICE_WIFI(device);
     NMDeviceWifiPrivate *priv                  = NM_DEVICE_WIFI_GET_PRIVATE(self);
     gs_unref_object NMSupplicantConfig *config = NULL;
-    NM80211Mode                         ap_mode;
+    _NM80211Mode                        ap_mode;
     NMActRequest *                      req;
     NMWifiAP *                          ap;
     NMConnection *                      connection;
@@ -3213,10 +3225,11 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
      * if the user didn't specify one and we didn't find an AP that matched
      * the connection, just pick a frequency the device supports.
      */
-    if (NM_IN_SET(ap_mode, NM_802_11_MODE_ADHOC, NM_802_11_MODE_MESH) || nm_wifi_ap_is_hotspot(ap))
+    if (NM_IN_SET(ap_mode, _NM_802_11_MODE_ADHOC, _NM_802_11_MODE_MESH)
+        || nm_wifi_ap_is_hotspot(ap))
         ensure_hotspot_frequency(self, s_wireless, ap);
 
-    if (ap_mode == NM_802_11_MODE_INFRA)
+    if (ap_mode == _NM_802_11_MODE_INFRA)
         set_powersave(device);
 
     /* Build up the supplicant configuration */
@@ -3333,7 +3346,7 @@ act_stage4_ip_config_timeout(NMDevice *           device,
     s_ip       = nm_connection_get_setting_ip_config(connection, addr_family);
     may_fail   = nm_setting_ip_config_get_may_fail(s_ip);
 
-    if (priv->mode == NM_802_11_MODE_AP)
+    if (priv->mode == _NM_802_11_MODE_AP)
         goto call_parent;
 
     if (may_fail || !is_static_wep(connection)) {
@@ -3686,8 +3699,8 @@ nm_device_wifi_init(NMDeviceWifi *self)
 
     priv->scan_last_request_started_at_msec = G_MININT64;
     priv->hidden_probe_scan_warn            = TRUE;
-    priv->mode                              = NM_802_11_MODE_INFRA;
-    priv->wowlan_restore                    = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
+    priv->mode                              = _NM_802_11_MODE_INFRA;
+    priv->wowlan_restore                    = _NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
 }
 
 static void
@@ -3698,7 +3711,7 @@ constructed(GObject *object)
 
     G_OBJECT_CLASS(nm_device_wifi_parent_class)->constructed(object);
 
-    if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP)
+    if (priv->capabilities & _NM_WIFI_DEVICE_CAP_AP)
         _LOGI(LOGD_PLATFORM | LOGD_WIFI, "driver supports Access Point (AP) mode");
 
     /* Connect to the supplicant manager */
@@ -3706,7 +3719,7 @@ constructed(GObject *object)
 }
 
 NMDevice *
-nm_device_wifi_new(const char *iface, NMDeviceWifiCapabilities capabilities)
+nm_device_wifi_new(const char *iface, _NMDeviceWifiCapabilities capabilities)
 {
     return g_object_new(NM_TYPE_DEVICE_WIFI,
                         NM_DEVICE_IFACE,
@@ -3814,9 +3827,9 @@ nm_device_wifi_class_init(NMDeviceWifiClass *klass)
     obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_WIFI_MODE,
                                                   "",
                                                   "",
-                                                  NM_802_11_MODE_UNKNOWN,
-                                                  NM_802_11_MODE_AP,
-                                                  NM_802_11_MODE_INFRA,
+                                                  _NM_802_11_MODE_UNKNOWN,
+                                                  _NM_802_11_MODE_AP,
+                                                  _NM_802_11_MODE_INFRA,
                                                   G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
     obj_properties[PROP_BITRATE] = g_param_spec_uint(NM_DEVICE_WIFI_BITRATE,
@@ -3847,7 +3860,7 @@ nm_device_wifi_class_init(NMDeviceWifiClass *klass)
                           "",
                           0,
                           G_MAXUINT32,
-                          NM_WIFI_DEVICE_CAP_NONE,
+                          _NM_WIFI_DEVICE_CAP_NONE,
                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
     obj_properties[PROP_SCANNING] = g_param_spec_boolean(NM_DEVICE_WIFI_SCANNING,
diff --git a/src/core/devices/wifi/nm-device-wifi.h b/src/core/devices/wifi/nm-device-wifi.h
index d9e9038c..c7ff1a14 100644
--- a/src/core/devices/wifi/nm-device-wifi.h
+++ b/src/core/devices/wifi/nm-device-wifi.h
@@ -33,7 +33,7 @@ typedef struct _NMDeviceWifiClass NMDeviceWifiClass;
 
 GType nm_device_wifi_get_type(void);
 
-NMDevice *nm_device_wifi_new(const char *iface, NMDeviceWifiCapabilities capabilities);
+NMDevice *nm_device_wifi_new(const char *iface, _NMDeviceWifiCapabilities capabilities);
 
 const CList *_nm_device_wifi_get_aps(NMDeviceWifi *self);
 
diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c
index b4b019d3..56386c57 100644
--- a/src/core/devices/wifi/nm-iwd-manager.c
+++ b/src/core/devices/wifi/nm-iwd-manager.c
@@ -8,14 +8,20 @@
 #include "nm-iwd-manager.h"
 
 #include <net/if.h>
+#include <glib/gstdio.h>
+#include <errno.h>
+#include <sys/stat.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-manager.h"
 #include "nm-device-iwd.h"
 #include "nm-wifi-utils.h"
-#include "nm-glib-aux/nm-random-utils.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-random-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 #include "settings/nm-settings.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
+#include "nm-config.h"
 
 /*****************************************************************************/
 
@@ -28,6 +34,7 @@ typedef struct {
 typedef struct {
     GDBusProxy *          known_network;
     NMSettingsConnection *mirror_connection;
+    const KnownNetworkId *id;
 } KnownNetworkData;
 
 typedef struct {
@@ -40,6 +47,8 @@ typedef struct {
     char *              agent_path;
     GHashTable *        known_networks;
     NMDeviceIwd *       last_agent_call_device;
+    char *              last_state_dir;
+    char *              warned_state_dir;
 } NMIwdManagerPrivate;
 
 struct _NMIwdManager {
@@ -419,25 +428,94 @@ known_network_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
     variant = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
     if (!variant) {
         nm_log_warn(LOGD_WIFI,
-                    "Updating %s on IWD known network %s failed: %s",
+                    "iwd: updating %s on IWD known network %s failed: %s",
                     (const char *) user_data,
                     g_dbus_proxy_get_object_path(G_DBUS_PROXY(source)),
                     error->message);
     }
 }
 
+static gboolean
+iwd_config_write(GKeyFile *             config,
+                 const char *           filepath,
+                 const struct timespec *mtime,
+                 GError **              error)
+{
+    gsize           length;
+    gs_free char *  data     = g_key_file_to_data(config, &length, NULL);
+    struct timespec times[2] = {{.tv_nsec = UTIME_OMIT}, *mtime};
+
+    /* Atomically write or replace the file with the right permission bits
+     * and timestamps set.  We rely on the temporary file created by
+     * nm_utils_file_set_contents having only upper-case letters and digits
+     * in the last few filename characters -- it cannot end in .open, .psk
+     * or .8021x.
+     */
+    return nm_utils_file_set_contents(filepath, data, length, 0600, times, NULL, error);
+}
+
+static const char *
+get_config_path(NMIwdManager *self)
+{
+    NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
+    const char *         path;
+
+    path = nm_config_data_get_iwd_config_path(NM_CONFIG_GET_DATA);
+    if (path && path[0] == '\0') {
+        nm_clear_g_free(&priv->warned_state_dir);
+        return NULL;
+    }
+
+    if (!path || nm_streq(path, "auto")) {
+        path = priv->last_state_dir;
+        if (!path) {
+            nm_clear_g_free(&priv->warned_state_dir);
+            return NULL;
+        }
+    }
+
+    if (priv->warned_state_dir && !nm_streq(priv->warned_state_dir, path))
+        nm_clear_g_free(&priv->warned_state_dir);
+
+    if (path && (path[0] != '/' || !g_file_test(path, G_FILE_TEST_IS_DIR))) {
+        if (!priv->warned_state_dir) {
+            priv->warned_state_dir = g_strdup(path);
+            _LOGW("IWD StateDirectory '%s' not accessible", priv->warned_state_dir);
+        }
+        return NULL;
+    }
+
+    return path;
+}
+
 static void
-sett_conn_changed(NMSettingsConnection *sett_conn, guint update_reason, KnownNetworkData *data)
+sett_conn_changed(NMSettingsConnection *  sett_conn,
+                  guint                   update_reason,
+                  const KnownNetworkData *data)
 {
     NMSettingsConnectionIntFlags flags;
-    NMConnection *               conn   = nm_settings_connection_get_connection(sett_conn);
-    NMSettingConnection *        s_conn = nm_connection_get_setting_connection(conn);
-    gboolean                     nm_autoconnectable = nm_setting_connection_get_autoconnect(s_conn);
-    gboolean iwd_autoconnectable = get_property_bool(data->known_network, "AutoConnect", TRUE);
+    NMConnection *               conn          = nm_settings_connection_get_connection(sett_conn);
+    NMSettingConnection *        s_conn        = nm_connection_get_setting_connection(conn);
+    NMSettingWireless *          s_wifi        = nm_connection_get_setting_wireless(conn);
+    nm_auto_unref_keyfile GKeyFile *iwd_config = NULL;
+    const char *                    iwd_dir;
+    gs_free char *                  filename  = NULL;
+    gs_free char *                  full_path = NULL;
+    gs_free_error GError *error               = NULL;
+    NMIwdNetworkSecurity  security;
+    GBytes *              ssid;
+    const guint8 *        ssid_data;
+    gsize                 ssid_len;
+    gboolean              removed;
+    GStatBuf              statbuf;
+    gboolean              have_mtime;
 
     nm_assert(sett_conn == data->mirror_connection);
 
-    if (iwd_autoconnectable == nm_autoconnectable)
+    if (!NM_FLAGS_ANY(update_reason,
+                      NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET
+                          | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS
+                          | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS))
         return;
 
     /* If this is a generated connection it may be ourselves updating it */
@@ -445,21 +523,131 @@ sett_conn_changed(NMSettingsConnection *sett_conn, guint update_reason, KnownNet
     if (NM_FLAGS_HAS(flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
         return;
 
+    iwd_dir = get_config_path(nm_iwd_manager_get());
+    if (!iwd_dir) {
+        gboolean nm_autoconnectable  = nm_setting_connection_get_autoconnect(s_conn);
+        gboolean iwd_autoconnectable = get_property_bool(data->known_network, "AutoConnect", TRUE);
+
+        if (iwd_autoconnectable != nm_autoconnectable) {
+            nm_log_dbg(LOGD_WIFI,
+                       "iwd: updating AutoConnect on known network at %s based on connection %s",
+                       g_dbus_proxy_get_object_path(data->known_network),
+                       nm_settings_connection_get_id(data->mirror_connection));
+            g_dbus_proxy_call(data->known_network,
+                              DBUS_INTERFACE_PROPERTIES ".Set",
+                              g_variant_new("(ssv)",
+                                            NM_IWD_KNOWN_NETWORK_INTERFACE,
+                                            "AutoConnect",
+                                            g_variant_new_boolean(nm_autoconnectable)),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              -1,
+                              NULL,
+                              known_network_update_cb,
+                              "AutoConnect");
+        }
+
+        return;
+    }
+
+    /* If the SSID and the security type in the NMSettingsConnection haven't
+     * changed, we just need to overwrite the original IWD config file.
+     * Otherwise we need to call Forget on the original KnownNetwork or
+     * remove its file.  IWD will have to delete one D-Bus object and
+     * create another anyway because the SSID and security type are in the
+     * D-Bus object path, so no point renaming the file.
+     */
+    ssid       = nm_setting_wireless_get_ssid(s_wifi);
+    ssid_data  = ssid ? g_bytes_get_data(ssid, &ssid_len) : NULL;
+    removed    = FALSE;
+    have_mtime = FALSE;
+
+    if (!nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &security)
+        || security != data->id->security || !ssid_data || ssid_len != strlen(data->id->name)
+        || memcmp(ssid_data, data->id->name, ssid_len)) {
+        gs_free char *orig_filename =
+            nm_wifi_utils_get_iwd_config_filename(data->id->name, -1, data->id->security);
+        gs_free char *orig_full_path = g_strdup_printf("%s/%s", iwd_dir, orig_filename);
+
+        if (g_stat(orig_full_path, &statbuf) == 0)
+            have_mtime = TRUE;
+
+        if (g_remove(orig_full_path) == 0)
+            nm_log_dbg(LOGD_WIFI, "iwd: profile at %s removed", orig_full_path);
+        else if (errno != ENOENT)
+            nm_log_dbg(LOGD_WIFI,
+                       "iwd: profile at %s not removed: %s (%i)",
+                       orig_full_path,
+                       strerror(errno),
+                       errno);
+
+        removed = TRUE;
+    }
+
+    if (!nm_streq(nm_settings_connection_get_connection_type(sett_conn), "802-11-wireless")
+        || !s_wifi)
+        return;
+
+    /* If the connection has any permissions other than the default we don't
+     * want to save it as an IWD profile.  IWD will make it available for
+     * everybody to attempt a connection, remove, or toggle "autoconnectable".
+     */
+    if (s_conn && nm_setting_connection_get_num_permissions(s_conn)) {
+        nm_log_dbg(
+            LOGD_WIFI,
+            "iwd: changed Wi-Fi connection %s not mirrored as IWD profile because of non-default "
+            "permissions",
+            nm_settings_connection_get_id(sett_conn));
+        return;
+    }
+
+    iwd_config = nm_wifi_utils_connection_to_iwd_config(conn, &filename, &error);
+    if (!iwd_config) {
+        /* The error message here is not translated and it only goes in
+         * the logs.
+         */
+        nm_log_dbg(LOGD_WIFI,
+                   "iwd: changed Wi-Fi connection %s not mirrored as IWD profile: %s",
+                   nm_settings_connection_get_id(sett_conn),
+                   error->message);
+        return;
+    }
+
+    full_path = g_strdup_printf("%s/%s", iwd_dir, filename);
+    if (removed && g_file_test(full_path, G_FILE_TEST_EXISTS)) {
+        nm_log_dbg(LOGD_WIFI,
+                   "iwd: changed Wi-Fi connection %s not mirrored as IWD profile because %s "
+                   "already exists",
+                   nm_settings_connection_get_id(sett_conn),
+                   full_path);
+        return;
+    }
+
+    if (!removed && g_stat(full_path, &statbuf) == 0)
+        have_mtime = TRUE;
+
+    /* If modifying an existing network try to preserve the file mtime,
+     * otherwise use a small non-zero timespec value to signal that the
+     * network is autoconnectable (according to its AutoConnect value)
+     * but hasn't recently been connected to and thus shouldn't be
+     * prioritized by autoconnect.
+     */
+    if (!have_mtime) {
+        statbuf.st_mtim.tv_sec  = 1;
+        statbuf.st_mtim.tv_nsec = 0;
+    }
+
+    if (!iwd_config_write(iwd_config, full_path, &statbuf.st_mtim, &error)) {
+        nm_log_dbg(LOGD_WIFI,
+                   "iwd: changed Wi-Fi connection %s not mirrored as IWD profile: save error: %s",
+                   nm_settings_connection_get_id(sett_conn),
+                   error->message);
+        return;
+    }
+
     nm_log_dbg(LOGD_WIFI,
-               "Updating AutoConnect on known network at %s based on connection %s",
-               g_dbus_proxy_get_object_path(data->known_network),
-               nm_settings_connection_get_id(data->mirror_connection));
-    g_dbus_proxy_call(data->known_network,
-                      DBUS_INTERFACE_PROPERTIES ".Set",
-                      g_variant_new("(ssv)",
-                                    NM_IWD_KNOWN_NETWORK_INTERFACE,
-                                    "AutoConnect",
-                                    g_variant_new_boolean(nm_autoconnectable)),
-                      G_DBUS_CALL_FLAGS_NONE,
-                      -1,
-                      NULL,
-                      known_network_update_cb,
-                      "AutoConnect");
+               "iwd: changed Wi-Fi connection %s mirrored as IWD profile %s",
+               nm_settings_connection_get_id(sett_conn),
+               full_path);
 }
 
 /* Look up an existing NMSettingsConnection for a network that has been
@@ -590,8 +778,10 @@ mirror_connection(NMIwdManager *        self,
                          NULL);
             g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_HIDDEN, hidden, NULL);
         } else {
-            KnownNetworkData data = {known_network, settings_connection};
-            sett_conn_changed(settings_connection, 0, &data);
+            KnownNetworkData data = {known_network, settings_connection, id};
+            sett_conn_changed(settings_connection,
+                              NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET,
+                              &data);
         }
     }
 
@@ -621,7 +811,7 @@ mirror_connection(NMIwdManager *        self,
                            NM_SETTING_CONNECTION_ID,
                            id->name,
                            NM_SETTING_CONNECTION_UUID,
-                           nm_utils_uuid_generate_buf(uuid),
+                           nm_uuid_generate_random_str_arr(uuid),
                            NM_SETTING_CONNECTION_AUTOCONNECT,
                            autoconnectable,
                            NULL);
@@ -779,6 +969,7 @@ interface_added(GDBusObjectManager *object_manager,
         } else {
             data                = g_slice_new0(KnownNetworkData);
             data->known_network = g_object_ref(proxy);
+            data->id            = id;
             g_hash_table_insert(priv->known_networks, id, data);
         }
 
@@ -907,6 +1098,9 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
     const guint8 *        ssid_bytes;
     gsize                 ssid_len;
     NMSettingsConnection *new_mirror_conn;
+    const char *          iwd_dir;
+    gs_free char *        filename  = NULL;
+    gs_free char *        full_path = NULL;
 
     if (!nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &id.security))
         return;
@@ -923,8 +1117,12 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
     ssid_buf[ssid_len] = '\0';
     id.name            = ssid_buf;
     data               = g_hash_table_lookup(priv->known_networks, &id);
-    if (!data)
-        return;
+    if (!data) {
+        if (!g_utf8_validate((const char *) ssid_bytes, ssid_len, NULL))
+            return;
+
+        goto try_delete_file;
+    }
 
     if (data->mirror_connection != sett_conn)
         return;
@@ -941,7 +1139,7 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
     }
 
     if (!priv->running)
-        return;
+        goto try_delete_file;
 
     g_dbus_proxy_call(data->known_network,
                       "Forget",
@@ -951,6 +1149,91 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
                       NULL,
                       NULL,
                       NULL);
+    return;
+
+try_delete_file:
+    if (mirror_connection(self, &id, FALSE, NULL))
+        return;
+
+    iwd_dir = get_config_path(self);
+    if (!iwd_dir)
+        return;
+
+    filename  = nm_wifi_utils_get_iwd_config_filename(id.name, ssid_len, id.security);
+    full_path = g_strdup_printf("%s/%s", iwd_dir, filename);
+    if (g_remove(full_path) == 0)
+        _LOGD("IWD profile at %s removed", full_path);
+    else if (errno != ENOENT)
+        _LOGD("IWD profile at %s not removed: %s (%i)", full_path, strerror(errno), errno);
+}
+
+static void
+connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer user_data)
+{
+    NMIwdManager *       self   = user_data;
+    NMConnection *       conn   = nm_settings_connection_get_connection(sett_conn);
+    NMSettingConnection *s_conn = nm_connection_get_setting_connection(conn);
+    const char *         iwd_dir;
+    gs_free char *       filename              = NULL;
+    gs_free char *       full_path             = NULL;
+    gs_free_error GError *error                = NULL;
+    nm_auto_unref_keyfile GKeyFile *iwd_config = NULL;
+    NMSettingsConnectionIntFlags    flags;
+
+    if (!nm_streq(nm_settings_connection_get_connection_type(sett_conn), "802-11-wireless"))
+        return;
+
+    iwd_dir = get_config_path(self);
+    if (!iwd_dir)
+        return;
+
+    /* If this is a generated connection it may be ourselves creating it and
+     * directly assigning it to a KnownNetwork's .mirror_connection.
+     */
+    flags = nm_settings_connection_get_flags(sett_conn);
+    if (NM_FLAGS_HAS(flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
+        return;
+
+    /* If the connection has any permissions other than the default we don't
+     * want to save it as an IWD profile.  IWD will make it available for
+     * everybody to attempt a connection, remove, or toggle "autoconnectable".
+     */
+    if (s_conn && nm_setting_connection_get_num_permissions(s_conn)) {
+        _LOGD("New Wi-Fi connection %s not mirrored as IWD profile because of non-default "
+              "permissions",
+              nm_settings_connection_get_id(sett_conn));
+        return;
+    }
+
+    iwd_config = nm_wifi_utils_connection_to_iwd_config(conn, &filename, &error);
+    if (!iwd_config) {
+        /* The error message here is not translated and it only goes in
+         * the logs.
+         */
+        _LOGD("New Wi-Fi connection %s not mirrored as IWD profile: %s",
+              nm_settings_connection_get_id(sett_conn),
+              error->message);
+        return;
+    }
+
+    full_path = g_strdup_printf("%s/%s", iwd_dir, filename);
+    if (g_file_test(full_path, G_FILE_TEST_EXISTS)) {
+        _LOGD("New Wi-Fi connection %s not mirrored as IWD profile because %s already exists",
+              nm_settings_connection_get_id(sett_conn),
+              full_path);
+        return;
+    }
+
+    if (!g_key_file_save_to_file(iwd_config, full_path, &error)) {
+        _LOGD("New Wi-Fi connection %s not mirrored as IWD profile: save error: %s",
+              nm_settings_connection_get_id(sett_conn),
+              error->message);
+        return;
+    }
+
+    _LOGD("New Wi-Fi connection %s mirrored as IWD profile %s",
+          nm_settings_connection_get_id(sett_conn),
+          full_path);
 }
 
 static gboolean
@@ -1115,10 +1398,11 @@ device_removed(NMManager *manager, NMDevice *device, gpointer user_data)
 static int
 object_compare_interfaces(gconstpointer a, gconstpointer b)
 {
-    static const char *interface_order[] = {
+    static const char *const interface_order[] = {
         NM_IWD_KNOWN_NETWORK_INTERFACE,
         NM_IWD_NETWORK_INTERFACE,
         NM_IWD_DEVICE_INTERFACE,
+        NULL,
     };
     int   rank_a = G_N_ELEMENTS(interface_order);
     int   rank_b = G_N_ELEMENTS(interface_order);
@@ -1145,6 +1429,56 @@ object_compare_interfaces(gconstpointer a, gconstpointer b)
 }
 
 static void
+get_daemon_info_cb(GObject *source, GAsyncResult *res, gpointer user_data)
+{
+    NMIwdManager *       self = user_data;
+    NMIwdManagerPrivate *priv;
+    gs_unref_variant GVariant *properties = NULL;
+    gs_free_error GError *error           = NULL;
+    GVariantIter *        properties_iter;
+    const char *          key;
+    GVariant *            value;
+
+    properties = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
+    if (!properties) {
+        if (nm_utils_error_is_cancelled(error))
+            return;
+
+        nm_log_warn(LOGD_WIFI, "iwd: Daemon.GetInfo() failed: %s", error->message);
+        return;
+    }
+
+    priv = NM_IWD_MANAGER_GET_PRIVATE(self);
+
+    if (!g_variant_is_of_type(properties, G_VARIANT_TYPE("(a{sv})"))) {
+        _LOGE("Daemon.GetInfo returned type %s instead of (a{sv})",
+              g_variant_get_type_string(properties));
+        return;
+    }
+
+    g_variant_get(properties, "(a{sv})", &properties_iter);
+
+    while (g_variant_iter_next(properties_iter, "{&sv}", &key, &value)) {
+        if (nm_streq(key, "StateDirectory")) {
+            if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
+                _LOGE("Daemon.GetInfo property %s is typed '%s' instead of 's'",
+                      key,
+                      g_variant_get_type_string(value));
+                goto next;
+            }
+
+            nm_clear_g_free(&priv->last_state_dir);
+            priv->last_state_dir = g_variant_dup_string(value, NULL);
+        }
+
+next:
+        g_variant_unref(value);
+    }
+
+    g_variant_iter_free(properties_iter);
+}
+
+static void
 got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data)
 {
     NMIwdManager *       self  = user_data;
@@ -1181,7 +1515,8 @@ got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data)
     }
 
     if (_om_has_name_owner(object_manager)) {
-        GList *objects, *iter;
+        GList *         objects, *iter;
+        gs_unref_object GDBusInterface *daemon = NULL;
 
         priv->running = true;
 
@@ -1207,6 +1542,19 @@ got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data)
 
         if (priv->agent_id)
             register_agent(self);
+
+        daemon = g_dbus_object_manager_get_interface(object_manager,
+                                                     "/net/connman/iwd", /* IWD 1.15+ */
+                                                     NM_IWD_DAEMON_INTERFACE);
+        if (daemon)
+            g_dbus_proxy_call(G_DBUS_PROXY(daemon),
+                              "GetInfo",
+                              g_variant_new("()"),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              -1,
+                              priv->cancellable,
+                              get_daemon_info_cb,
+                              self);
     }
 }
 
@@ -1304,11 +1652,31 @@ nm_iwd_manager_init(NMIwdManager *self)
     g_signal_connect(priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK(device_added), self);
     g_signal_connect(priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK(device_removed), self);
 
+    /* The current logic is that we track all creations and removals but
+     * for modifications we only listen to those connections that are
+     * currently a KnownNetwork's mirror_connection.  There may be multiple
+     * NMSettingsConnections referring to the same SSID+Security type tuple
+     * so to the same KnownNetwork.  So to make connection profile editing
+     * work at least for the simple cases, we track one NMSettingsConnection
+     * out of those, and we map its changes to the IWD KnownNetwork.
+     *
+     * When an NMSettingsConnection is created by a user for a completely
+     * new network and the settings are compatible with IWD, we create an
+     * IWD KnownNetwork config file for it.  IWD will notice that and a
+     * KnownNetwork objects pops up on D-Bus.  We look up a suitable
+     * mirror_connection for it and only then subscribe to modification
+     * signals.  There are various different ways that this could be done,
+     * it's not clear which one's the best.
+     */
     priv->settings = g_object_ref(NM_SETTINGS_GET);
     g_signal_connect(priv->settings,
                      NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
                      G_CALLBACK(connection_removed),
                      self);
+    g_signal_connect(priv->settings,
+                     NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
+                     G_CALLBACK(connection_added),
+                     self);
 
     priv->cancellable = g_cancellable_new();
 
@@ -1347,6 +1715,9 @@ dispose(GObject *object)
 
     priv->last_agent_call_device = NULL;
 
+    nm_clear_g_free(&priv->last_state_dir);
+    nm_clear_g_free(&priv->warned_state_dir);
+
     G_OBJECT_CLASS(nm_iwd_manager_parent_class)->dispose(object);
 }
 
diff --git a/src/core/devices/wifi/nm-iwd-manager.h b/src/core/devices/wifi/nm-iwd-manager.h
index 466f67c7..e794e661 100644
--- a/src/core/devices/wifi/nm-iwd-manager.h
+++ b/src/core/devices/wifi/nm-iwd-manager.h
@@ -13,6 +13,7 @@
 #define NM_IWD_BUS_TYPE G_BUS_TYPE_SYSTEM
 #define NM_IWD_SERVICE  "net.connman.iwd"
 
+#define NM_IWD_DAEMON_INTERFACE        "net.connman.iwd.Daemon"
 #define NM_IWD_AGENT_MANAGER_INTERFACE "net.connman.iwd.AgentManager"
 #define NM_IWD_WIPHY_INTERFACE         "net.connman.iwd.Adapter"
 #define NM_IWD_DEVICE_INTERFACE        "net.connman.iwd.Device"
diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c
index 08fa10ec..20ed6833 100644
--- a/src/core/devices/wifi/nm-wifi-ap.c
+++ b/src/core/devices/wifi/nm-wifi-ap.c
@@ -13,13 +13,13 @@
 
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
-#include "nm-glib-aux/nm-ref-string.h"
+#include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-setting-wireless.h"
 #include "nm-utils.h"
 #include "nm-wifi-utils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "supplicant/nm-supplicant-interface.h"
 
 #define PROTO_WPA "wpa"
@@ -41,12 +41,12 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMWifiAP,
 
 struct _NMWifiAPPrivate {
     /* Scanned or cached values */
-    GBytes *    ssid;
-    char *      address;
-    NM80211Mode mode;
-    guint8      strength;
-    guint32     freq;        /* Frequency in MHz; ie 2412 (== 2.412 GHz) */
-    guint32     max_bitrate; /* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
+    GBytes *     ssid;
+    char *       address;
+    _NM80211Mode mode;
+    guint8       strength;
+    guint32      freq;        /* Frequency in MHz; ie 2412 (== 2.412 GHz) */
+    guint32      max_bitrate; /* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
 
     gint64
         last_seen_msec; /* Timestamp when the AP was seen lastly (in nm_utils_get_monotonic_timestamp_*() scale).
@@ -98,7 +98,7 @@ nm_wifi_ap_set_ssid(NMWifiAP *ap, GBytes *ssid)
     }
 
     l = g_bytes_get_size(ssid);
-    if (l == 0 || l > 32)
+    if (l == 0 || l > NM_IW_ESSID_MAX_SIZE)
         g_return_val_if_reached(FALSE);
 
     priv = NM_WIFI_AP_GET_PRIVATE(ap);
@@ -191,24 +191,24 @@ nm_wifi_ap_set_address(NMWifiAP *ap, const char *addr)
     return nm_wifi_ap_set_address_bin(ap, &addr_buf);
 }
 
-NM80211Mode
+_NM80211Mode
 nm_wifi_ap_get_mode(NMWifiAP *ap)
 {
-    g_return_val_if_fail(NM_IS_WIFI_AP(ap), NM_802_11_MODE_UNKNOWN);
+    g_return_val_if_fail(NM_IS_WIFI_AP(ap), _NM_802_11_MODE_UNKNOWN);
 
     return NM_WIFI_AP_GET_PRIVATE(ap)->mode;
 }
 
 static gboolean
-nm_wifi_ap_set_mode(NMWifiAP *ap, NM80211Mode mode)
+nm_wifi_ap_set_mode(NMWifiAP *ap, _NM80211Mode mode)
 {
     NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
 
     nm_assert(NM_IN_SET(mode,
-                        NM_802_11_MODE_UNKNOWN,
-                        NM_802_11_MODE_ADHOC,
-                        NM_802_11_MODE_INFRA,
-                        NM_802_11_MODE_MESH));
+                        _NM_802_11_MODE_UNKNOWN,
+                        _NM_802_11_MODE_ADHOC,
+                        _NM_802_11_MODE_INFRA,
+                        _NM_802_11_MODE_MESH));
 
     if (priv->mode != mode) {
         priv->mode = mode;
@@ -513,34 +513,35 @@ nm_wifi_ap_to_string(const NMWifiAP *self, char *str_buf, gulong buf_len, gint64
 
     nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
 
-    g_snprintf(str_buf,
-               buf_len,
-               "%17s %-35s [ %c %3u %3u%% %c%c %c%c W:%04X R:%04X ] %s sup:%s [nm:%s]",
-               priv->address ?: "(none)",
-               (ssid_to_free = _nm_utils_ssid_to_string(priv->ssid)),
-               (priv->mode == NM_802_11_MODE_ADHOC
-                    ? '*'
-                    : (priv->hotspot
-                           ? '#'
-                           : (priv->fake ? 'f' : (priv->mode == NM_802_11_MODE_MESH ? 'm' : 'a')))),
-               chan,
-               priv->strength,
-               priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_',
-               priv->metered ? 'M' : '_',
-               priv->flags & NM_802_11_AP_FLAGS_WPS ? 'W' : '_',
-               priv->flags & NM_802_11_AP_FLAGS_WPS_PIN
-                   ? 'p'
-                   : (priv->flags & NM_802_11_AP_FLAGS_WPS_PBC ? '#' : '_'),
-               priv->wpa_flags & 0xFFFF,
-               priv->rsn_flags & 0xFFFF,
-               priv->last_seen_msec != G_MININT64
-                   ? nm_sprintf_buf(str_buf_ts,
-                                    "%3u.%03us",
-                                    (guint)((now_msec - priv->last_seen_msec) / 1000),
-                                    (guint)((now_msec - priv->last_seen_msec) % 1000))
-                   : "        ",
-               supplicant_id,
-               export_path);
+    g_snprintf(
+        str_buf,
+        buf_len,
+        "%17s %-35s [ %c %3u %3u%% %c%c %c%c W:%04X R:%04X ] %s sup:%s [nm:%s]",
+        priv->address ?: "(none)",
+        (ssid_to_free = _nm_utils_ssid_to_string_gbytes(priv->ssid)),
+        (priv->mode == _NM_802_11_MODE_ADHOC
+             ? '*'
+             : (priv->hotspot
+                    ? '#'
+                    : (priv->fake ? 'f' : (priv->mode == _NM_802_11_MODE_MESH ? 'm' : 'a')))),
+        chan,
+        priv->strength,
+        priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_',
+        priv->metered ? 'M' : '_',
+        priv->flags & NM_802_11_AP_FLAGS_WPS ? 'W' : '_',
+        priv->flags & NM_802_11_AP_FLAGS_WPS_PIN
+            ? 'p'
+            : (priv->flags & NM_802_11_AP_FLAGS_WPS_PBC ? '#' : '_'),
+        priv->wpa_flags & 0xFFFF,
+        priv->rsn_flags & 0xFFFF,
+        priv->last_seen_msec != G_MININT64
+            ? nm_sprintf_buf(str_buf_ts,
+                             "%3u.%03us",
+                             (guint) ((now_msec - priv->last_seen_msec) / 1000),
+                             (guint) ((now_msec - priv->last_seen_msec) % 1000))
+            : "        ",
+        supplicant_id,
+        export_path);
     return str_buf;
 }
 
@@ -589,13 +590,13 @@ nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection)
 
     mode = nm_setting_wireless_get_mode(s_wireless);
     if (mode) {
-        if (!strcmp(mode, "infrastructure") && (priv->mode != NM_802_11_MODE_INFRA))
+        if (!strcmp(mode, "infrastructure") && (priv->mode != _NM_802_11_MODE_INFRA))
             return FALSE;
-        if (!strcmp(mode, "adhoc") && (priv->mode != NM_802_11_MODE_ADHOC))
+        if (!strcmp(mode, "adhoc") && (priv->mode != _NM_802_11_MODE_ADHOC))
             return FALSE;
-        if (!strcmp(mode, "ap") && (priv->mode != NM_802_11_MODE_INFRA || priv->hotspot != TRUE))
+        if (!strcmp(mode, "ap") && (priv->mode != _NM_802_11_MODE_INFRA || priv->hotspot != TRUE))
             return FALSE;
-        if (!strcmp(mode, "mesh") && (priv->mode != NM_802_11_MODE_MESH))
+        if (!strcmp(mode, "mesh") && (priv->mode != _NM_802_11_MODE_MESH))
             return FALSE;
     }
 
@@ -624,7 +625,7 @@ nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection)
                                                       priv->flags,
                                                       priv->wpa_flags,
                                                       priv->rsn_flags,
-                                                      priv->mode);
+                                                      NM_802_11_MODE_CAST(priv->mode));
 }
 
 gboolean
@@ -713,7 +714,7 @@ nm_wifi_ap_init(NMWifiAP *self)
 
     c_list_init(&self->aps_lst);
 
-    priv->mode           = NM_802_11_MODE_INFRA;
+    priv->mode           = _NM_802_11_MODE_INFRA;
     priv->flags          = NM_802_11_AP_FLAGS_NONE;
     priv->wpa_flags      = NM_802_11_AP_SEC_NONE;
     priv->rsn_flags      = NM_802_11_AP_SEC_NONE;
@@ -758,19 +759,19 @@ nm_wifi_ap_new_fake_from_connection(NMConnection *connection)
     mode = nm_setting_wireless_get_mode(s_wireless);
     if (mode) {
         if (!strcmp(mode, "infrastructure"))
-            nm_wifi_ap_set_mode(ap, NM_802_11_MODE_INFRA);
+            nm_wifi_ap_set_mode(ap, _NM_802_11_MODE_INFRA);
         else if (!strcmp(mode, "adhoc")) {
-            nm_wifi_ap_set_mode(ap, NM_802_11_MODE_ADHOC);
+            nm_wifi_ap_set_mode(ap, _NM_802_11_MODE_ADHOC);
             adhoc = TRUE;
         } else if (!strcmp(mode, "mesh"))
-            nm_wifi_ap_set_mode(ap, NM_802_11_MODE_MESH);
+            nm_wifi_ap_set_mode(ap, _NM_802_11_MODE_MESH);
         else if (!strcmp(mode, "ap")) {
-            nm_wifi_ap_set_mode(ap, NM_802_11_MODE_INFRA);
+            nm_wifi_ap_set_mode(ap, _NM_802_11_MODE_INFRA);
             NM_WIFI_AP_GET_PRIVATE(ap)->hotspot = TRUE;
         } else
             goto error;
     } else {
-        nm_wifi_ap_set_mode(ap, NM_802_11_MODE_INFRA);
+        nm_wifi_ap_set_mode(ap, _NM_802_11_MODE_INFRA);
     }
 
     band    = nm_setting_wireless_get_band(s_wireless);
@@ -861,27 +862,21 @@ finalize(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_access_point = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_ACCESS_POINT,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Flags", "u", NM_WIFI_AP_FLAGS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WpaFlags", "u", NM_WIFI_AP_WPA_FLAGS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RsnFlags", "u", NM_WIFI_AP_RSN_FLAGS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ssid", "ay", NM_WIFI_AP_SSID),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Frequency",
-                                                             "u",
-                                                             NM_WIFI_AP_FREQUENCY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_WIFI_AP_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "u", NM_WIFI_AP_MODE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("MaxBitrate",
-                                                             "u",
-                                                             NM_WIFI_AP_MAX_BITRATE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Strength", "y", NM_WIFI_AP_STRENGTH),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("LastSeen",
-                                                             "i",
-                                                             NM_WIFI_AP_LAST_SEEN), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Flags", "u", NM_WIFI_AP_FLAGS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WpaFlags", "u", NM_WIFI_AP_WPA_FLAGS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RsnFlags", "u", NM_WIFI_AP_RSN_FLAGS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ssid", "ay", NM_WIFI_AP_SSID),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Frequency", "u", NM_WIFI_AP_FREQUENCY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_WIFI_AP_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "u", NM_WIFI_AP_MODE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("MaxBitrate",
+                                                           "u",
+                                                           NM_WIFI_AP_MAX_BITRATE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Strength", "y", NM_WIFI_AP_STRENGTH),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("LastSeen",
+                                                           "i",
+                                                           NM_WIFI_AP_LAST_SEEN), ), ),
 };
 
 static void
@@ -955,9 +950,9 @@ nm_wifi_ap_class_init(NMWifiAPClass *ap_class)
     obj_properties[PROP_MODE] = g_param_spec_uint(NM_WIFI_AP_MODE,
                                                   "",
                                                   "",
-                                                  NM_802_11_MODE_ADHOC,
-                                                  NM_802_11_MODE_INFRA,
-                                                  NM_802_11_MODE_INFRA,
+                                                  _NM_802_11_MODE_ADHOC,
+                                                  _NM_802_11_MODE_INFRA,
+                                                  _NM_802_11_MODE_INFRA,
                                                   G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
     obj_properties[PROP_MAX_BITRATE] = g_param_spec_uint(NM_WIFI_AP_MAX_BITRATE,
diff --git a/src/core/devices/wifi/nm-wifi-ap.h b/src/core/devices/wifi/nm-wifi-ap.h
index bdd72415..bffd28a8 100644
--- a/src/core/devices/wifi/nm-wifi-ap.h
+++ b/src/core/devices/wifi/nm-wifi-ap.h
@@ -10,6 +10,7 @@
 #include "nm-dbus-object.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
+#include "libnm-base/nm-base.h"
 
 #define NM_TYPE_WIFI_AP            (nm_wifi_ap_get_type())
 #define NM_WIFI_AP(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_AP, NMWifiAP))
@@ -69,7 +70,7 @@ gboolean               nm_wifi_ap_set_ssid(NMWifiAP *ap, GBytes *ssid);
 const char *           nm_wifi_ap_get_address(const NMWifiAP *ap);
 gboolean               nm_wifi_ap_set_address(NMWifiAP *ap, const char *addr);
 gboolean               nm_wifi_ap_set_address_bin(NMWifiAP *ap, const NMEtherAddr *addr);
-NM80211Mode            nm_wifi_ap_get_mode(NMWifiAP *ap);
+_NM80211Mode           nm_wifi_ap_get_mode(NMWifiAP *ap);
 gboolean               nm_wifi_ap_is_hotspot(NMWifiAP *ap);
 gint8                  nm_wifi_ap_get_strength(NMWifiAP *ap);
 gboolean               nm_wifi_ap_set_strength(NMWifiAP *ap, gint8 strength);
diff --git a/src/core/devices/wifi/nm-wifi-common.c b/src/core/devices/wifi/nm-wifi-common.c
index c715c07c..b98ef222 100644
--- a/src/core/devices/wifi/nm-wifi-common.c
+++ b/src/core/devices/wifi/nm-wifi-common.c
@@ -146,31 +146,25 @@ const NMDBusInterfaceInfoExtended nm_interface_info_device_wireless = {
                     .in_args = NM_DEFINE_GDBUS_ARG_INFOS(
                         NM_DEFINE_GDBUS_ARG_INFO("options", "a{sv}"), ), ),
                 .handle = impl_device_wifi_request_scan, ), ),
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &nm_signal_info_wireless_access_point_added,
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_wireless_access_point_added,
                                                 &nm_signal_info_wireless_access_point_removed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
-                                                             "s",
-                                                             NM_DEVICE_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("PermHwAddress",
-                                                             "s",
-                                                             NM_DEVICE_PERM_HW_ADDRESS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "u", NM_DEVICE_WIFI_MODE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Bitrate",
-                                                             "u",
-                                                             NM_DEVICE_WIFI_BITRATE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("AccessPoints",
-                                                             "ao",
-                                                             NM_DEVICE_WIFI_ACCESS_POINTS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ActiveAccessPoint",
-                                                             "o",
-                                                             NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WirelessCapabilities",
-                                                             "u",
-                                                             NM_DEVICE_WIFI_CAPABILITIES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("PermHwAddress",
+                                                           "s",
+                                                           NM_DEVICE_PERM_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "u", NM_DEVICE_WIFI_MODE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Bitrate", "u", NM_DEVICE_WIFI_BITRATE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AccessPoints",
+                                                           "ao",
+                                                           NM_DEVICE_WIFI_ACCESS_POINTS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ActiveAccessPoint",
+                                                           "o",
+                                                           NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WirelessCapabilities",
+                                                           "u",
+                                                           NM_DEVICE_WIFI_CAPABILITIES),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("LastScan",
                                                            "x",
                                                            NM_DEVICE_WIFI_LAST_SCAN), ), ),
-    .legacy_property_changed = TRUE,
 };
diff --git a/src/core/devices/wifi/nm-wifi-factory.c b/src/core/devices/wifi/nm-wifi-factory.c
index 40375e1c..d2269c74 100644
--- a/src/core/devices/wifi/nm-wifi-factory.c
+++ b/src/core/devices/wifi/nm-wifi-factory.c
@@ -15,7 +15,7 @@
 #include "nm-device-olpc-mesh.h"
 #include "nm-device-iwd.h"
 #include "settings/nm-settings-connection.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-config.h"
 
 /*****************************************************************************/
@@ -74,7 +74,8 @@ create_device(NMDeviceFactory *     factory,
               NMConnection *        connection,
               gboolean *            out_ignore)
 {
-    gs_free char *backend = NULL;
+    gs_free char *backend_free = NULL;
+    const char *  backend;
 
     g_return_val_if_fail(iface != NULL, NULL);
     g_return_val_if_fail(plink != NULL, NULL);
@@ -89,7 +90,7 @@ create_device(NMDeviceFactory *     factory,
                                                          plink,
                                                          "wifi",
                                                          NULL);
-    nm_strstrip(backend);
+    backend = nm_strstrip_avoid_copy_a(300, backend, &backend_free);
 
     nm_log_dbg(LOGD_PLATFORM | LOGD_WIFI,
                "(%s) config: backend is %s%s%s%s",
@@ -97,9 +98,9 @@ create_device(NMDeviceFactory *     factory,
                NM_PRINT_FMT_QUOTE_STRING(backend),
                WITH_IWD ? " (iwd support enabled)" : "");
     if (!backend || !g_ascii_strcasecmp(backend, "wpa_supplicant")) {
-        NMDevice *               device;
-        NMDeviceWifiCapabilities capabilities;
-        NM80211Mode              mode;
+        NMDevice *                device;
+        _NMDeviceWifiCapabilities capabilities;
+        _NM80211Mode              mode;
 
         if (!nm_platform_wifi_get_capabilities(NM_PLATFORM_GET, plink->ifindex, &capabilities)) {
             nm_log_warn(LOGD_PLATFORM | LOGD_WIFI,
@@ -114,7 +115,7 @@ create_device(NMDeviceFactory *     factory,
          * them if/when they change to a handled type.
          */
         mode = nm_platform_wifi_get_mode(NM_PLATFORM_GET, plink->ifindex);
-        if (mode == NM_802_11_MODE_UNKNOWN) {
+        if (mode == _NM_802_11_MODE_UNKNOWN) {
             *out_ignore = TRUE;
             return NULL;
         }
diff --git a/src/core/devices/wifi/nm-wifi-p2p-peer.c b/src/core/devices/wifi/nm-wifi-p2p-peer.c
index 8488f32d..433c2833 100644
--- a/src/core/devices/wifi/nm-wifi-p2p-peer.c
+++ b/src/core/devices/wifi/nm-wifi-p2p-peer.c
@@ -12,13 +12,13 @@
 
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
-#include "nm-glib-aux/nm-ref-string.h"
+#include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-setting-wireless.h"
 #include "nm-utils.h"
 #include "nm-wifi-utils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "supplicant/nm-supplicant-types.h"
 
 /*****************************************************************************/
@@ -608,7 +608,6 @@ static const NMDBusInterfaceInfoExtended interface_info_p2p_peer = {
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("LastSeen",
                                                            "i",
                                                            NM_WIFI_P2P_PEER_LAST_SEEN), ), ),
-    .legacy_property_changed = FALSE,
 };
 
 static void
diff --git a/src/core/devices/wifi/nm-wifi-utils.c b/src/core/devices/wifi/nm-wifi-utils.c
index aed236cc..15ced990 100644
--- a/src/core/devices/wifi/nm-wifi-utils.c
+++ b/src/core/devices/wifi/nm-wifi-utils.c
@@ -7,10 +7,14 @@
 
 #include "nm-wifi-utils.h"
 
+#include <arpa/inet.h>
+#include <netinet/if_ether.h>
 #include <stdlib.h>
 
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
+#include "libnm-base/nm-config-base.h"
 
 static gboolean
 verify_no_wep(NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error)
@@ -526,7 +530,7 @@ verify_adhoc(NMSettingWirelessSecurity *s_wsec,
 gboolean
 nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
                                   const char *  bssid,
-                                  NM80211Mode   ap_mode,
+                                  _NM80211Mode  ap_mode,
                                   guint32       ap_freq,
                                   guint32       ap_flags,
                                   guint32       ap_wpa_flags,
@@ -575,14 +579,14 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
         /* Make sure the supplied mode matches the AP's */
         if (!strcmp(mode, NM_SETTING_WIRELESS_MODE_INFRA)
             || !strcmp(mode, NM_SETTING_WIRELESS_MODE_AP)) {
-            if (ap_mode == NM_802_11_MODE_INFRA)
+            if (ap_mode == _NM_802_11_MODE_INFRA)
                 valid = TRUE;
         } else if (!strcmp(mode, NM_SETTING_WIRELESS_MODE_ADHOC)) {
-            if (ap_mode == NM_802_11_MODE_ADHOC)
+            if (ap_mode == _NM_802_11_MODE_ADHOC)
                 valid = TRUE;
             adhoc = TRUE;
         } else if (!strcmp(mode, NM_SETTING_WIRELESS_MODE_MESH)) {
-            if (ap_mode == NM_802_11_MODE_MESH)
+            if (ap_mode == _NM_802_11_MODE_MESH)
                 valid = TRUE;
             mesh = TRUE;
         }
@@ -600,10 +604,10 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
         }
     } else {
         mode = NM_SETTING_WIRELESS_MODE_INFRA;
-        if (ap_mode == NM_802_11_MODE_ADHOC) {
+        if (ap_mode == _NM_802_11_MODE_ADHOC) {
             mode  = NM_SETTING_WIRELESS_MODE_ADHOC;
             adhoc = TRUE;
-        } else if (ap_mode == NM_802_11_MODE_MESH) {
+        } else if (ap_mode == _NM_802_11_MODE_MESH) {
             mode = NM_SETTING_WIRELESS_MODE_MESH;
             mesh = TRUE;
         }
@@ -810,15 +814,24 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
          * setting.  Since there's so much configuration required for it, there's
          * no way it can be automatically completed.
          */
-    } else if ((key_mgmt && !strcmp(key_mgmt, "sae"))
-               || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) {
+    } else if (nm_streq0(key_mgmt, "wpa-psk")
+               || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE
+                   && (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK
+                       || ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK))) {
+        g_object_set(s_wsec,
+                     NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
+                     "wpa-psk",
+                     NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
+                     "open",
+                     NULL);
+    } else if (nm_streq0(key_mgmt, "sae") || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) {
         g_object_set(s_wsec,
                      NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
                      "sae",
                      NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
                      "open",
                      NULL);
-    } else if ((key_mgmt && !strcmp(key_mgmt, "owe"))
+    } else if (nm_streq0(key_mgmt, "owe")
                || NM_FLAGS_ANY(ap_rsn_flags,
                                NM_802_11_AP_SEC_KEY_MGMT_OWE | NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) {
         g_object_set(s_wsec,
@@ -827,9 +840,8 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
                      NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
                      "open",
                      NULL);
-    } else if ((key_mgmt && !strcmp(key_mgmt, "wpa-psk"))
-               || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
-               || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
+    } else if (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK
+               || ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) {
         g_object_set(s_wsec,
                      NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
                      "wpa-psk",
@@ -839,7 +851,7 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
         /* Leave proto/pairwise/group as client set them; if they are unset the
          * supplicant will figure out the best combination at connect time.
          */
-    } else if ((key_mgmt && !strcmp(key_mgmt, "wpa-eap-suite-b-192"))
+    } else if (nm_streq0(key_mgmt, "wpa-eap-suite-b-192")
                || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192)) {
         g_object_set(s_wsec,
                      NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
@@ -943,3 +955,865 @@ nm_wifi_connection_get_iwd_ssid_and_security(NMConnection *        connection,
 
     return TRUE;
 }
+
+/*****************************************************************************/
+
+/* Builds the IWD network configuration file name for a given SSID
+ * and security type pair.  The SSID should be valid UTF-8 and in
+ * any case must contain no NUL-bytes.  If @ssid is NUL-terminated,
+ * @ssid_len can be -1 instead of actual SSID length.
+ */
+char *
+nm_wifi_utils_get_iwd_config_filename(const char *         ssid,
+                                      gssize               ssid_len,
+                                      NMIwdNetworkSecurity security)
+{
+    const char *security_suffix;
+    const char *ptr;
+    gboolean    alnum_ssid = TRUE;
+
+    for (ptr = ssid; ssid_len != 0 && *ptr != '\0'; ptr++, ssid_len--)
+        if (!g_ascii_isalnum(*ptr) && !strchr("-_ ", *ptr))
+            alnum_ssid = FALSE;
+
+    g_return_val_if_fail(ptr != ssid && ptr - ssid <= NM_IW_ESSID_MAX_SIZE, NULL);
+
+    switch (security) {
+    case NM_IWD_NETWORK_SECURITY_OPEN:
+        security_suffix = "open";
+        break;
+    case NM_IWD_NETWORK_SECURITY_PSK:
+        security_suffix = "psk";
+        break;
+    case NM_IWD_NETWORK_SECURITY_8021X:
+        security_suffix = "8021x";
+        break;
+    default:
+        return NULL;
+    }
+
+    if (alnum_ssid) {
+        return g_strdup_printf("%.*s.%s", (int) (ptr - ssid), ssid, security_suffix);
+    } else {
+        char ssid_buf[NM_IW_ESSID_MAX_SIZE * 2 + 1];
+
+        return g_strdup_printf("=%s.%s",
+                               nm_utils_bin2hexstr_full(ssid, ptr - ssid, '\0', FALSE, ssid_buf),
+                               security_suffix);
+    }
+}
+
+/*****************************************************************************/
+
+#define SECRETS_DONT_STORE_FLAGS \
+    (NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED)
+
+static gboolean
+psk_setting_to_iwd_config(GKeyFile *file, NMSettingWirelessSecurity *s_wsec, GError **error)
+{
+    NMSettingSecretFlags psk_flags = nm_setting_wireless_security_get_psk_flags(s_wsec);
+    const char *         psk       = nm_setting_wireless_security_get_psk(s_wsec);
+    gsize                psk_len;
+    guint8               buffer[32];
+    const char *         key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec);
+
+    if (!psk || NM_FLAGS_ANY(psk_flags, SECRETS_DONT_STORE_FLAGS)) {
+        if (NM_FLAGS_ANY(psk_flags, SECRETS_DONT_STORE_FLAGS)) {
+            nm_log_info(
+                LOGD_WIFI,
+                "IWD network config is being created wihout the PSK but IWD will save the PSK on "
+                "successful activation not honoring the psk-flags property");
+        }
+        return TRUE;
+    }
+
+    psk_len = strlen(psk);
+    if (nm_streq0(key_mgmt, "sae")) {
+        g_key_file_set_string(file, "Security", "Passphrase", psk);
+    } else if (psk_len >= 8 && psk_len <= 63) {
+        g_key_file_set_string(file, "Security", "Passphrase", psk);
+    } else if (psk_len == 64 && nm_utils_hexstr2bin_buf(psk, FALSE, FALSE, NULL, buffer)) {
+        g_key_file_set_string(file, "Security", "PreSharedKey", psk);
+    } else {
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Unknown PSK format");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+eap_certs_to_iwd_config(GKeyFile *      file,
+                        NMSetting8021x *s_8021x,
+                        bool            phase2,
+                        char *          iwd_prefix,
+                        GError **       error)
+{
+    NMSetting8021xCKScheme ca_cert_scheme =
+        phase2 ? nm_setting_802_1x_get_phase2_ca_cert_scheme(s_8021x)
+               : nm_setting_802_1x_get_ca_cert_scheme(s_8021x);
+    NMSetting8021xCKScheme client_cert_scheme =
+        phase2 ? nm_setting_802_1x_get_phase2_client_cert_scheme(s_8021x)
+               : nm_setting_802_1x_get_client_cert_scheme(s_8021x);
+    NMSetting8021xCKScheme key_scheme;
+    NMSettingSecretFlags   key_password_flags;
+    const char *           ca_path = phase2 ? nm_setting_802_1x_get_phase2_ca_path(s_8021x)
+                                            : nm_setting_802_1x_get_ca_path(s_8021x);
+    const char *           cert_path;
+    const char *           key_path = NULL;
+    const char *           key_password;
+    const char *           domain_suffix_match;
+    const char *           domain_match;
+    char                   setting_buf[128];
+
+    /* TODO: should check that all certificates and the key are RSA */
+    /* Note: up to IWD 1.9 only the PEM encoding was supported for certificates
+     * and only PKCS#8 PEM for keys but we don't know the IWD version here.
+     * From IWD 1.10 raw (DER) X.509 certificates and PKCS#12 are also supported
+     * for certificates but a certificate list or chain still has to be PEM
+     * (i.e. if it contains more than one certificate.)  Raw PKCS#12 and
+     * old-style OpenSSL PEM formats are also supported for keys.  Hopefully
+     * this is in practice the same set of file:// formats as supported by
+     * nm_crypto_* / wpa_supplicant so we need no conversions here.
+     */
+
+    if (nm_setting_802_1x_get_system_ca_certs(s_8021x)) {
+        /* Either overrides or is added to the certificates in (phase2-)ca-cert
+         * and ca-path depending on whether it points to a file or a directory.
+         * We can't ignore this property so it's an error if it is set.
+         * Fortunately not used by nm-connection-editor.
+         */
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "The system-ca-certs property is not supported");
+        return FALSE;
+    }
+
+    if (ca_path) {
+        /* To support this (and this could be applied to system-ca-certs as
+         * well) we'd have to scan the directory, parse the certificates and
+         * write a new certificate-list file to point to in the IWD config.
+         * This is going to create issues of where to store these files, for
+         * how long and with what permission bits.  Fortunately this doesn't
+         * seem to be used by nm-connection-editor either.
+         *
+         * That file would also have to contain whatever the (phase2-)ca-cert
+         * propterty points to because IWD has only one CACert setting per
+         * phase.
+         */
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "The (phase2-)ca-path property is not supported");
+        return FALSE;
+    }
+
+    if (ca_cert_scheme != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
+        if (ca_cert_scheme != NM_SETTING_802_1X_CK_SCHEME_PATH) {
+            /* To support the blob scheme we'd have to either convert the
+             * certificate data into a PEM payload and embed the PEM file in
+             * the IWD config file, which is not supported by GKeyFile, or write
+             * it into a new file to point to in the IWD config.  This is again
+             * is going to create issues of where to store these files, for how
+             * long and with what permission bits.  Fortunately this scheme isn't
+             * used in nm-connection-editor either.
+             *
+             * PKCS#11 is not supported by IWD in any way so we don't need to
+             * support the PKCS#11 URI scheme.
+             *
+             * If scheme is unknown, assume no value is set.
+             */
+            g_set_error_literal(
+                error,
+                NM_CONNECTION_ERROR,
+                NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                "(phase2-)ca-cert property schemes other than file:// not supported");
+            return FALSE;
+        }
+
+        cert_path = phase2 ? nm_setting_802_1x_get_phase2_ca_cert_path(s_8021x)
+                           : nm_setting_802_1x_get_ca_cert_path(s_8021x);
+        if (cert_path)
+            g_key_file_set_string(file,
+                                  "Security",
+                                  nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "CACert"),
+                                  cert_path);
+    }
+
+    if (client_cert_scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN)
+        goto private_key_done;
+
+    if (client_cert_scheme != NM_SETTING_802_1X_CK_SCHEME_PATH) {
+        g_set_error_literal(
+            error,
+            NM_CONNECTION_ERROR,
+            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+            "(phase2-)client-cert property schemes other than file:// not supported");
+        return FALSE;
+    }
+
+    cert_path = phase2 ? nm_setting_802_1x_get_phase2_client_cert_path(s_8021x)
+                       : nm_setting_802_1x_get_client_cert_path(s_8021x);
+    if (!cert_path)
+        goto private_key_done;
+    g_key_file_set_string(file,
+                          "Security",
+                          nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ClientCert"),
+                          cert_path);
+
+    key_scheme = phase2 ? nm_setting_802_1x_get_phase2_private_key_scheme(s_8021x)
+                        : nm_setting_802_1x_get_private_key_scheme(s_8021x);
+    if (key_scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+        key_path = phase2 ? nm_setting_802_1x_get_phase2_private_key_path(s_8021x)
+                          : nm_setting_802_1x_get_private_key_path(s_8021x);
+    if (key_scheme != NM_SETTING_802_1X_CK_SCHEME_PATH || !key_path) {
+        /* The same comments apply to writing the key into a temporary file
+         * as for the certificates (above), except this is even more
+         * sensitive.
+         */
+        g_set_error_literal(
+            error,
+            NM_CONNECTION_ERROR,
+            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+            "(phase2-)private-key property schemes other than file:// not supported");
+        return FALSE;
+    }
+    g_key_file_set_string(file,
+                          "Security",
+                          nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ClientKey"),
+                          key_path);
+
+    key_password       = phase2 ? nm_setting_802_1x_get_phase2_private_key_password(s_8021x)
+                                : nm_setting_802_1x_get_private_key_password(s_8021x);
+    key_password_flags = phase2 ? nm_setting_802_1x_get_phase2_private_key_password_flags(s_8021x)
+                                : nm_setting_802_1x_get_private_key_password_flags(s_8021x);
+    if (!key_password || NM_FLAGS_ANY(key_password_flags, SECRETS_DONT_STORE_FLAGS)) {
+        g_key_file_set_comment(
+            file,
+            "Security",
+            setting_buf,
+            "ClientKeyPassphrase not to be saved, will be queried through the agent if needed",
+            NULL);
+        goto private_key_done;
+    }
+    g_key_file_set_string(file,
+                          "Security",
+                          nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ClientKeyPassphrase"),
+                          key_password);
+
+private_key_done:
+    if (phase2 ? nm_setting_802_1x_get_phase2_subject_match(s_8021x)
+               : nm_setting_802_1x_get_subject_match(s_8021x)) {
+        g_set_error_literal(
+            error,
+            NM_CONNECTION_ERROR,
+            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+            "(phase2-)subject-match not supported, use domain-match or domain-suffix-match");
+        return FALSE;
+    }
+
+    if (phase2 ? nm_setting_802_1x_get_num_phase2_altsubject_matches(s_8021x)
+               : nm_setting_802_1x_get_num_altsubject_matches(s_8021x)) {
+        /* We could convert the "DNS:" entries into a ServerDomainMask but we'd
+         * have to leave out the "EMAIL:" and "URI:" types or report error.
+         * The interpretation still wouldn't be exactly the same as in
+         * wpa_supplicant.
+         */
+        g_set_error_literal(
+            error,
+            NM_CONNECTION_ERROR,
+            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+            "(phase2-)altsubject-matches not supported, use domain-match or domain-suffix-match");
+        return FALSE;
+    }
+
+    domain_suffix_match = phase2 ? nm_setting_802_1x_get_phase2_domain_suffix_match(s_8021x)
+                                 : nm_setting_802_1x_get_domain_suffix_match(s_8021x);
+    domain_match        = phase2 ? nm_setting_802_1x_get_phase2_domain_match(s_8021x)
+                                 : nm_setting_802_1x_get_domain_match(s_8021x);
+
+    if (domain_suffix_match || domain_match) {
+        GString *   s = g_string_sized_new(128);
+        const char *ptr;
+        const char *end;
+
+        for (ptr = domain_suffix_match; ptr; ptr = *end == ';' ? end + 1 : NULL) {
+            if (s->len)
+                g_string_append_c(s, ';');
+            end = strchrnul(ptr, ';');
+            /* Use *.<suffix> to get the suffix match effect */
+            g_string_append(s, "*.");
+            g_string_append_len(s, ptr, end - ptr);
+        }
+
+        /* domain-match can be appended as-is */
+        if (domain_match) {
+            if (s->len)
+                g_string_append_c(s, ';');
+            g_string_append(s, domain_match);
+        }
+
+        g_key_file_set_string(file,
+                              "Security",
+                              nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ServerDomainMask"),
+                              s->str);
+        g_string_free(s, TRUE);
+    }
+
+    return TRUE;
+}
+
+static void
+eap_method_name_to_iwd_config(GKeyFile *file, const char *iwd_prefix, const char *method)
+{
+    char setting_buf[128];
+
+    g_key_file_set_string(file,
+                          "Security",
+                          nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Method"),
+                          method);
+}
+
+static void
+eap_optional_identity_to_iwd_config(GKeyFile *file, const char *iwd_prefix, const char *identity)
+{
+    char setting_buf[128];
+
+    /* The identity is optional for some methods where an authenticator may
+     * in theory not ask for it.  For our usage here we treat it as always
+     * optional because it can be omitted in the config file if the user
+     * wants IWD to query for it on every connection.
+     */
+    if (identity) {
+        g_key_file_set_string(file,
+                              "Security",
+                              nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Identity"),
+                              identity);
+    } else {
+        g_key_file_set_comment(
+            file,
+            "Security",
+            nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Method"),
+            "Identity not to be saved, will be queried through the agent if needed",
+            NULL);
+    }
+}
+
+static gboolean
+eap_optional_password_to_iwd_config(GKeyFile *      file,
+                                    const char *    iwd_prefix,
+                                    NMSetting8021x *s_8021x,
+                                    GError **       error)
+{
+    char                 setting_buf[128];
+    const char *         password = nm_setting_802_1x_get_password(s_8021x);
+    NMSettingSecretFlags flags    = nm_setting_802_1x_get_password_flags(s_8021x);
+
+    if (!password && nm_setting_802_1x_get_password_raw(s_8021x)) {
+        /* IWD doesn't support passwords that can't be encoded in the config
+         * file, i.e. containing NUL characters.  Those that don't have NULs
+         * could in theory be written to the config file but GKeyFile may not
+         * like that if they're no UTF-8, and the password-raw property is
+         * not written by nm-connection-editor anyway.
+         */
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Non-UTF-8 passwords are not supported, if the password is UTF-8 set "
+                            "the \"password\" property");
+        return FALSE;
+    }
+    if (!password || NM_FLAGS_ANY(flags, SECRETS_DONT_STORE_FLAGS)) {
+        return g_key_file_set_comment(file,
+                                      "Security",
+                                      nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Method"),
+                                      "Password not to be saved, will be queried through the agent",
+                                      error);
+    } else {
+        g_key_file_set_string(file,
+                              "Security",
+                              nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Password"),
+                              password);
+        return TRUE;
+    }
+}
+
+static void
+eap_phase1_identity_to_iwd_config(GKeyFile *file, const char *iwd_prefix, NMSetting8021x *s_8021x)
+{
+    const char *phase1_identity = nm_setting_802_1x_get_anonymous_identity(s_8021x);
+
+    if (!phase1_identity) {
+        phase1_identity = nm_setting_802_1x_get_identity(s_8021x);
+
+        if (phase1_identity) {
+            nm_log_info(LOGD_WIFI,
+                        "IWD network config will send the same EAP Identity string in "
+                        "plaintext in phase 1 as in phase 2 (encrypted) to mimic legacy "
+                        "behavior, set [%s].%s=anonymous to prevent exposing the value",
+                        NM_SETTING_802_1X_SETTING_NAME,
+                        NM_SETTING_802_1X_ANONYMOUS_IDENTITY);
+        }
+    }
+
+    eap_optional_identity_to_iwd_config(file, iwd_prefix, phase1_identity);
+}
+
+static gboolean
+eap_method_config_to_iwd_config(GKeyFile *      file,
+                                NMSetting8021x *s_8021x,
+                                gboolean        phase2,
+                                const char *    method,
+                                const char *    iwd_prefix,
+                                GError **       error)
+{
+    char prefix_buf[128];
+
+    if (nm_streq0(method, "tls")) {
+        eap_method_name_to_iwd_config(file, iwd_prefix, "TLS");
+        eap_optional_identity_to_iwd_config(file,
+                                            iwd_prefix,
+                                            nm_setting_802_1x_get_identity(s_8021x));
+
+        return eap_certs_to_iwd_config(file,
+                                       s_8021x,
+                                       phase2,
+                                       nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "TLS-"),
+                                       error);
+    } else if (nm_streq0(method, "ttls") && !phase2) {
+        const char *noneap_method = nm_setting_802_1x_get_phase2_auth(s_8021x);
+
+        eap_method_name_to_iwd_config(file, iwd_prefix, "TTLS");
+        eap_phase1_identity_to_iwd_config(file, iwd_prefix, s_8021x);
+
+        if (!eap_certs_to_iwd_config(file,
+                                     s_8021x,
+                                     phase2,
+                                     nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "TTLS-"),
+                                     error))
+            return FALSE;
+
+        nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "TTLS-Phase2-");
+
+        if (nm_setting_802_1x_get_phase2_autheap(s_8021x)) {
+            if (noneap_method) {
+                g_set_error_literal(error,
+                                    NM_CONNECTION_ERROR,
+                                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                                    "Only one TTLS phase 2 method can be set");
+                return FALSE;
+            }
+            return eap_method_config_to_iwd_config(file,
+                                                   s_8021x,
+                                                   TRUE,
+                                                   nm_setting_802_1x_get_phase2_autheap(s_8021x),
+                                                   prefix_buf,
+                                                   error);
+        }
+
+        if (NM_IN_STRSET(noneap_method, "chap", "mschap", "mschapv2", "pap")) {
+            const char *iwd_method;
+
+            if (nm_streq0(noneap_method, "chap")) {
+                iwd_method = "Tunneled-CHAP";
+            } else if (nm_streq0(noneap_method, "mschap")) {
+                iwd_method = "Tunneled-MSCHAP";
+            } else if (nm_streq0(noneap_method, "mschapv2")) {
+                iwd_method = "Tunneled-MSCHAPv2";
+            } else {
+                iwd_method = "Tunneled-PAP";
+            }
+
+            eap_method_name_to_iwd_config(file, prefix_buf, iwd_method);
+            eap_optional_identity_to_iwd_config(file,
+                                                prefix_buf,
+                                                nm_setting_802_1x_get_identity(s_8021x));
+            return eap_optional_password_to_iwd_config(file, prefix_buf, s_8021x, error);
+        }
+
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Unsupported TTLS non-EAP inner method");
+        return FALSE;
+    } else if (nm_streq0(method, "peap") && !phase2) {
+        eap_method_name_to_iwd_config(file, iwd_prefix, "PEAP");
+        eap_phase1_identity_to_iwd_config(file, iwd_prefix, s_8021x);
+
+        if (!eap_certs_to_iwd_config(file,
+                                     s_8021x,
+                                     phase2,
+                                     nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "PEAP-"),
+                                     error))
+            return FALSE;
+
+        if (nm_setting_802_1x_get_phase1_peapver(s_8021x)
+            || nm_setting_802_1x_get_phase1_peaplabel(s_8021x))
+            nm_log_info(LOGD_WIFI,
+                        "IWD network config will not honour the PEAP version and label properties "
+                        "in the 802.1x setting (unsupported)");
+
+        if (!nm_setting_802_1x_get_phase2_auth(s_8021x)) {
+            /* Apparently PEAP can be used without a phase 2 but this is not
+             * supported by either NM or IWD.
+             */
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                                "PEAP without an inner method is unsupported");
+            return FALSE;
+        }
+
+        return eap_method_config_to_iwd_config(
+            file,
+            s_8021x,
+            TRUE,
+            nm_setting_802_1x_get_phase2_auth(s_8021x),
+            nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "PEAP-Phase2-"),
+            error);
+    } else if (nm_streq0(method, "md5") && phase2) {
+        eap_method_name_to_iwd_config(file, iwd_prefix, "MD5");
+        eap_optional_identity_to_iwd_config(file,
+                                            iwd_prefix,
+                                            nm_setting_802_1x_get_identity(s_8021x));
+        return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
+    } else if (nm_streq0(method, "gtc") && phase2) {
+        eap_method_name_to_iwd_config(file, iwd_prefix, "GTC");
+        eap_optional_identity_to_iwd_config(file,
+                                            iwd_prefix,
+                                            nm_setting_802_1x_get_identity(s_8021x));
+        return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
+    } else if (nm_streq0(method, "pwd")) {
+        eap_method_name_to_iwd_config(file, iwd_prefix, "PWD");
+        eap_optional_identity_to_iwd_config(file,
+                                            iwd_prefix,
+                                            nm_setting_802_1x_get_identity(s_8021x));
+        return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
+    } else if (nm_streq0(method, "mschapv2")) {
+        eap_method_name_to_iwd_config(file, iwd_prefix, "MSCHAPV2");
+        eap_optional_identity_to_iwd_config(file,
+                                            iwd_prefix,
+                                            nm_setting_802_1x_get_identity(s_8021x));
+        /* In this case we can support password-raw but would have to
+         * MD4-hash it and set as <iwd_prefix>Password-Hash
+         */
+        return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
+    } else if (nm_streq0(method, "external")) {
+        /* This may be a connection created by NMIwdManager in whch case there
+         * may be no need to be convert it back to the IWD format.  Ideally we
+         * would still rewrite the other sections/groups in the IWD settings
+         * file and preserve the [Security] group -- TODO.  Possibly this should
+         * also not be reported as an error.
+         */
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Connection contains no EAP method configuration");
+        return FALSE;
+    } else {
+        /* Some methods are only allowed in phase 1 or only phase 2.
+         * OTP, LEAP and FAST are not supported by IWD at all.
+         */
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            phase2 ? "Unsupported phase 2 EAP method"
+                                   : "Unsupported phase 1 EAP method");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+eap_setting_to_iwd_config(GKeyFile *file, NMSetting8021x *s_8021x, GError **error)
+{
+    const char *method;
+
+    if (!s_8021x || nm_setting_802_1x_get_num_eap_methods(s_8021x) == 0) {
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "The 802.1x setting is missing or no EAP method set");
+        return FALSE;
+    }
+
+    if (!nm_setting_verify(NM_SETTING(s_8021x), NULL, error))
+        return FALSE;
+
+    method = nm_setting_802_1x_get_eap_method(s_8021x, 0);
+
+    if (nm_setting_802_1x_get_num_eap_methods(s_8021x) > 1)
+        nm_log_info(LOGD_WIFI,
+                    "IWD network config will only contain the first EAP method: %s",
+                    method);
+
+    if (nm_setting_802_1x_get_phase1_auth_flags(s_8021x))
+        nm_log_info(LOGD_WIFI,
+                    "IWD network config will not honour the TLSv1.x-disable flags in the 802.1x "
+                    "setting (unsupported)");
+
+    if (nm_setting_802_1x_get_auth_timeout(s_8021x))
+        nm_log_info(LOGD_WIFI,
+                    "IWD network config will not honour the auth-timeout property in the 802.1x "
+                    "setting (unsupported)");
+
+    return eap_method_config_to_iwd_config(file, s_8021x, FALSE, method, "EAP-", error);
+}
+
+static gboolean
+ip4_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error)
+{
+    guint          num;
+    struct in_addr ip;
+
+    /* These settings are not acutally used unless global
+     * [General].EnableNetworkConfiguration is true, which we don't support.
+     * We add them for sake of completness, although many NMSettingIPConfig
+     * configurations can't be mapped to IWD configs and we simply ignore
+     * them.  If they were to be used we'd need to add a few warnings.
+     */
+
+    if (!s_ip)
+        return TRUE;
+
+    num = nm_setting_ip_config_get_num_dns(s_ip);
+    if (num) {
+        nm_auto_free_gstring GString *s = g_string_sized_new(128);
+        guint                         i;
+
+        for (i = 0; i < num; i++) {
+            if (s->len)
+                g_string_append_c(s, ' ');
+            g_string_append(s, nm_setting_ip_config_get_dns(s_ip, i));
+        }
+        /* It doesn't matter whether we add the DNS under [IPv4] or [IPv6]
+         * except that with method=auto the list will override the
+         * DNS addresses received over the DHCP version corresponing to
+         * v4 or v6.
+         * Note ignore-auto-dns=false isn't supported, this list always
+         * overrides the DHCP DNSes.
+         */
+        g_key_file_set_string(file, "IPv4", "DNS", s->str);
+    }
+
+    if (!nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
+        return TRUE;
+
+    num = nm_setting_ip_config_get_num_addresses(s_ip);
+    if (num) {
+        NMIPAddress *addr    = nm_setting_ip_config_get_address(s_ip, 0);
+        guint        prefix  = nm_ip_address_get_prefix(addr);
+        in_addr_t    netmask = _nm_utils_ip4_prefix_to_netmask(prefix);
+        char         buf[INET_ADDRSTRLEN];
+
+        nm_ip_address_get_address_binary(addr, &ip);
+        g_key_file_set_string(file, "IPv4", "Address", nm_ip_address_get_address(addr));
+        g_key_file_set_string(file, "IPv4", "Netmask", _nm_utils_inet4_ntop(netmask, buf));
+    } else {
+        inet_pton(AF_INET, "10.42.0.100", &ip);
+        g_key_file_set_string(file, "IPv4", "Address", "10.42.0.100");
+    }
+
+    if (nm_setting_ip_config_get_gateway(s_ip)) {
+        g_key_file_set_string(file, "IPv4", "Gateway", nm_setting_ip_config_get_gateway(s_ip));
+    } else {
+        uint32_t val;
+        char     buf[INET_ADDRSTRLEN];
+
+        /* IWD won't enable static IP unless both Address and Gateway are
+         * set so generate a gateway address if not known.
+         */
+        val = (ntohl(ip.s_addr) & 0xfffffff0) + 1;
+        if (val == ntohl(ip.s_addr))
+            val += 1;
+        g_key_file_set_string(file, "IPv4", "Gateway", _nm_utils_inet4_ntop(htonl(val), buf));
+    }
+
+    return TRUE;
+}
+
+static gboolean
+ip6_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error)
+{
+    guint        num;
+    NMIPAddress *addr;
+    char         buf[INET6_ADDRSTRLEN + 10];
+
+    if (!s_ip)
+        return TRUE;
+
+    num = nm_setting_ip_config_get_num_dns(s_ip);
+    if (num) {
+        nm_auto_free_gstring GString *s = g_string_sized_new(128);
+        guint                         i;
+
+        for (i = 0; i < num; i++) {
+            if (s->len)
+                g_string_append_c(s, ' ');
+            g_string_append(s, nm_setting_ip_config_get_dns(s_ip, i));
+        }
+        g_key_file_set_string(file, "IPv6", "DNS", s->str);
+    }
+
+    if (!NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip),
+                      NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+                      NM_SETTING_IP6_CONFIG_METHOD_DHCP,
+                      NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
+        return TRUE;
+
+    g_key_file_set_boolean(file, "IPv6", "Enabled", TRUE);
+
+    if (!nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
+        return TRUE;
+
+    if (!nm_setting_ip_config_get_num_addresses(s_ip)) {
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "IP address required for IPv6 manual config");
+        return FALSE;
+    }
+
+    addr = nm_setting_ip_config_get_address(s_ip, 0);
+    g_key_file_set_string(file,
+                          "IPv6",
+                          "Address",
+                          nm_sprintf_buf(buf,
+                                         "%s/%u",
+                                         nm_ip_address_get_address(addr),
+                                         nm_ip_address_get_prefix(addr)));
+    if (nm_setting_ip_config_get_gateway(s_ip))
+        g_key_file_set_string(file, "IPv6", "Gateway", nm_setting_ip_config_get_gateway(s_ip));
+    return TRUE;
+}
+
+GKeyFile *
+nm_wifi_utils_connection_to_iwd_config(NMConnection *connection,
+                                       char **       out_filename,
+                                       GError **     error)
+{
+    NMSettingConnection * s_conn = nm_connection_get_setting_connection(connection);
+    NMSettingWireless *   s_wifi = nm_connection_get_setting_wireless(connection);
+    GBytes *              ssid;
+    const guint8 *        ssid_data;
+    gsize                 ssid_len;
+    NMIwdNetworkSecurity  security;
+    const char *          cloned_mac_addr;
+    gs_free char *        comment        = NULL;
+    nm_auto_unref_keyfile GKeyFile *file = NULL;
+
+    if (!s_conn || !s_wifi
+        || !nm_streq(nm_setting_connection_get_connection_type(s_conn),
+                     NM_SETTING_WIRELESS_SETTING_NAME)) {
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Connection and/or wireless settings are missing");
+        return NULL;
+    }
+
+    if (!NM_IN_STRSET(nm_setting_wireless_get_mode(s_wifi), NULL, NM_SETTING_WIRELESS_MODE_INFRA)) {
+        g_set_error_literal(
+            error,
+            NM_CONNECTION_ERROR,
+            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+            "Non-infrastructure-mode connections don't have IWD profiles (or aren't supported)");
+        return NULL;
+    }
+
+    ssid      = nm_setting_wireless_get_ssid(s_wifi);
+    ssid_data = ssid ? g_bytes_get_data(ssid, &ssid_len) : NULL;
+    if (!ssid_data || ssid_len <= 0 || ssid_len > NM_IW_ESSID_MAX_SIZE
+        || !g_utf8_validate((const char *) ssid_data, ssid_len, NULL)) {
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Empty or non-UTF-8 SSIDs not supported by IWD");
+        return NULL;
+    }
+
+    if (!nm_wifi_connection_get_iwd_ssid_and_security(connection, NULL, &security)) {
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Connection's security type unrecognised");
+        return NULL;
+    }
+
+    file = g_key_file_new();
+
+    comment = g_strdup_printf(" Auto-generated from NetworkManager connection \"%s\"\n"
+                              " Changes to that connection overwrite this file when "
+                              "enabled by NM's [%s].%s value",
+                              nm_setting_connection_get_id(s_conn),
+                              NM_CONFIG_KEYFILE_GROUP_MAIN,
+                              NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH);
+    g_key_file_set_comment(file, NULL, NULL, comment, NULL);
+
+    if (!nm_setting_connection_get_autoconnect(s_conn))
+        g_key_file_set_boolean(file, "Settings", "AutoConnect", FALSE);
+
+    if (nm_setting_wireless_get_hidden(s_wifi))
+        g_key_file_set_boolean(file, "Settings", "Hidden", TRUE);
+
+    /* Only effective if IWD's global [General].AddressRandomization is set
+     * to "network".  "random" maps to [Settings].AlwaysRandomizeAddress=true,
+     * "stable" is the default, specific address maps to
+     * [Settings].AddressOverride set to that address.  "permanent" is not
+     * supported and "preserve" can only be achieved using the global
+     * [General].AddressRandomization=disabled setting.  We don't print
+     * warnings when we can't map the value here because we don't know what
+     * IWD's [General].AddressRandomization is set to.
+     */
+    cloned_mac_addr = nm_setting_wireless_get_cloned_mac_address(s_wifi);
+    if (nm_streq0(cloned_mac_addr, NM_CLONED_MAC_RANDOM))
+        g_key_file_set_boolean(file, "Settings", "AlwaysRandomizeAddress", TRUE);
+    else if (cloned_mac_addr && nm_utils_hwaddr_valid(cloned_mac_addr, ETH_ALEN))
+        g_key_file_set_string(file, "Settings", "AddressOverride", cloned_mac_addr);
+
+    if (!ip4_config_to_iwd_config(
+            file,
+            NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip4_config(connection)),
+            error))
+        return NULL;
+
+    if (!ip6_config_to_iwd_config(
+            file,
+            NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip6_config(connection)),
+            error))
+        return NULL;
+
+    switch (security) {
+    case NM_IWD_NETWORK_SECURITY_OPEN:
+        break;
+    case NM_IWD_NETWORK_SECURITY_PSK:
+        if (!psk_setting_to_iwd_config(file,
+                                       nm_connection_get_setting_wireless_security(connection),
+                                       error))
+            return NULL;
+
+        break;
+    case NM_IWD_NETWORK_SECURITY_8021X:
+        if (!eap_setting_to_iwd_config(file, nm_connection_get_setting_802_1x(connection), error))
+            return NULL;
+
+        break;
+    default:
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            "Connection security type is not supported");
+        return NULL;
+    }
+
+    if (out_filename)
+        *out_filename =
+            nm_wifi_utils_get_iwd_config_filename((const char *) ssid_data, ssid_len, security);
+
+    return g_steal_pointer(&file);
+}
diff --git a/src/core/devices/wifi/nm-wifi-utils.h b/src/core/devices/wifi/nm-wifi-utils.h
index 474bea41..2664c03e 100644
--- a/src/core/devices/wifi/nm-wifi-utils.h
+++ b/src/core/devices/wifi/nm-wifi-utils.h
@@ -11,6 +11,7 @@
 #include "nm-setting-wireless.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-8021x.h"
+#include "libnm-base/nm-base.h"
 
 typedef enum {
     NM_IWD_NETWORK_SECURITY_OPEN,
@@ -21,7 +22,7 @@ typedef enum {
 
 gboolean nm_wifi_utils_complete_connection(GBytes *      ssid,
                                            const char *  bssid,
-                                           NM80211Mode   mode,
+                                           _NM80211Mode  mode,
                                            guint32       ap_freq,
                                            guint32       flags,
                                            guint32       wpa_flags,
@@ -35,5 +36,11 @@ gboolean nm_wifi_utils_is_manf_default_ssid(GBytes *ssid);
 gboolean nm_wifi_connection_get_iwd_ssid_and_security(NMConnection *        connection,
                                                       char **               ssid,
                                                       NMIwdNetworkSecurity *security);
+char *   nm_wifi_utils_get_iwd_config_filename(const char *         ssid,
+                                               gssize               ssid_len,
+                                               NMIwdNetworkSecurity security);
+
+GKeyFile *
+nm_wifi_utils_connection_to_iwd_config(NMConnection *conn, char **out_filename, GError **error);
 
 #endif /* __NM_WIFI_UTILS_H__ */
diff --git a/src/core/devices/wifi/tests/test-devices-wifi.c b/src/core/devices/wifi/tests/test-devices-wifi.c
index bc0ba126..dc278c94 100644
--- a/src/core/devices/wifi/tests/test-devices-wifi.c
+++ b/src/core/devices/wifi/tests/test-devices-wifi.c
@@ -7,7 +7,7 @@
 
 #include "devices/wifi/nm-wifi-utils.h"
 #include "devices/wifi/nm-device-wifi.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "nm-test-utils-core.h"
 
@@ -49,7 +49,7 @@
 static gboolean
 complete_connection(const char *  ssid,
                     const char *  bssid,
-                    NM80211Mode   mode,
+                    _NM80211Mode  mode,
                     guint32       flags,
                     guint32       wpa_flags,
                     guint32       rsn_flags,
@@ -197,7 +197,7 @@ fill_8021x(NMConnection *connection, const KeyData items[])
 }
 
 static NMConnection *
-create_basic(const char *ssid, const char *bssid, NM80211Mode mode)
+create_basic(const char *ssid, const char *bssid, _NM80211Mode mode)
 {
     NMConnection *     connection;
     NMSettingWireless *s_wifi = NULL;
@@ -217,9 +217,9 @@ create_basic(const char *ssid, const char *bssid, NM80211Mode mode)
     if (bssid)
         g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL);
 
-    if (mode == NM_802_11_MODE_INFRA)
+    if (mode == _NM_802_11_MODE_INFRA)
         g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_MODE, "infrastructure", NULL);
-    else if (mode == NM_802_11_MODE_ADHOC)
+    else if (mode == _NM_802_11_MODE_ADHOC)
         g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_MODE, "adhoc", NULL);
     else
         g_assert_not_reached();
@@ -241,14 +241,14 @@ test_lock_bssid(void)
     src      = nm_simple_connection_new();
     success  = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_NONE,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
                                   TRUE,
                                   src,
                                   &error);
-    expected = create_basic(ssid, bssid, NM_802_11_MODE_INFRA);
+    expected = create_basic(ssid, bssid, _NM_802_11_MODE_INFRA);
     COMPARE(src, expected, success, error, 0, 0);
 
     g_object_unref(src);
@@ -273,14 +273,14 @@ test_open_ap_empty_connection(void)
     src      = nm_simple_connection_new();
     success  = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_NONE,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
                                   FALSE,
                                   src,
                                   &error);
-    expected = create_basic(ssid, NULL, NM_802_11_MODE_INFRA);
+    expected = create_basic(ssid, NULL, _NM_802_11_MODE_INFRA);
     COMPARE(src, expected, success, error, 0, 0);
 
     g_object_unref(src);
@@ -311,7 +311,7 @@ test_open_ap_leap_connection_1(gconstpointer add_wifi)
 
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_NONE,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -345,7 +345,7 @@ test_open_ap_leap_connection_2(void)
 
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_NONE,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -383,7 +383,7 @@ test_open_ap_wep_connection(gconstpointer add_wifi)
     fill_wsec(src, src_wsec);
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_NONE,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -427,7 +427,7 @@ test_ap_wpa_psk_connection_base(const char *  key_mgmt,
     fill_wsec(src, both_wsec);
     success = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   flags,
                                   wpa_flags,
                                   rsn_flags,
@@ -553,7 +553,7 @@ test_ap_wpa_eap_connection_base(const char *key_mgmt,
     fill_8021x(src, src_empty);
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   flags,
                                   wpa_flags,
                                   rsn_flags,
@@ -751,7 +751,7 @@ test_priv_ap_empty_connection(void)
     src     = nm_simple_connection_new();
     success = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -760,7 +760,7 @@ test_priv_ap_empty_connection(void)
                                   &error);
 
     /* Static WEP connection expected */
-    expected = create_basic(ssid, NULL, NM_802_11_MODE_INFRA);
+    expected = create_basic(ssid, NULL, _NM_802_11_MODE_INFRA);
     fill_wsec(expected, exp_wsec);
     COMPARE(src, expected, success, error, 0, 0);
 
@@ -798,7 +798,7 @@ test_priv_ap_leap_connection_1(gconstpointer add_wifi)
     fill_wsec(src, src_wsec);
     success = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -809,7 +809,7 @@ test_priv_ap_leap_connection_1(gconstpointer add_wifi)
      * there's no way to determine from the AP's beacon whether it's static WEP,
      * dynamic WEP, or LEAP.
      */
-    expected = create_basic(ssid, NULL, NM_802_11_MODE_INFRA);
+    expected = create_basic(ssid, NULL, _NM_802_11_MODE_INFRA);
     fill_wsec(expected, exp_wsec);
     COMPARE(src, expected, success, error, 0, 0);
 
@@ -840,7 +840,7 @@ test_priv_ap_leap_connection_2(void)
     fill_wsec(src, src_wsec);
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -885,7 +885,7 @@ test_priv_ap_dynamic_wep_1(void)
     fill_8021x(src, both_8021x);
     success = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -894,7 +894,7 @@ test_priv_ap_dynamic_wep_1(void)
                                   &error);
 
     /* We expect a completed Dynamic WEP connection */
-    expected = create_basic(ssid, NULL, NM_802_11_MODE_INFRA);
+    expected = create_basic(ssid, NULL, _NM_802_11_MODE_INFRA);
     fill_wsec(expected, exp_wsec);
     fill_8021x(expected, both_8021x);
     COMPARE(src, expected, success, error, 0, 0);
@@ -933,7 +933,7 @@ test_priv_ap_dynamic_wep_2(void)
     fill_8021x(src, both_8021x);
     success = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -942,7 +942,7 @@ test_priv_ap_dynamic_wep_2(void)
                                   &error);
 
     /* We expect a completed Dynamic WEP connection */
-    expected = create_basic(ssid, NULL, NM_802_11_MODE_INFRA);
+    expected = create_basic(ssid, NULL, _NM_802_11_MODE_INFRA);
     fill_wsec(expected, exp_wsec);
     fill_8021x(expected, both_8021x);
     COMPARE(src, expected, success, error, 0, 0);
@@ -976,7 +976,7 @@ test_priv_ap_dynamic_wep_3(void)
     fill_8021x(src, src_8021x);
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   NM_802_11_AP_SEC_NONE,
                                   NM_802_11_AP_SEC_NONE,
@@ -1102,7 +1102,7 @@ test_wpa_ap_empty_connection(gconstpointer data)
     src     = nm_simple_connection_new();
     success = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   wpa_flags_for_idx(idx),
                                   rsn_flags_for_idx(idx),
@@ -1111,7 +1111,7 @@ test_wpa_ap_empty_connection(gconstpointer data)
                                   &error);
 
     /* WPA connection expected */
-    expected = create_basic(ssid, NULL, NM_802_11_MODE_INFRA);
+    expected = create_basic(ssid, NULL, _NM_802_11_MODE_INFRA);
     fill_wsec(expected, exp_wsec);
     COMPARE(src, expected, success, error, 0, 0);
 
@@ -1144,7 +1144,7 @@ test_wpa_ap_leap_connection_1(gconstpointer data)
     fill_wsec(src, src_wsec);
     success = complete_connection(ssid,
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   wpa_flags_for_idx(idx),
                                   rsn_flags_for_idx(idx),
@@ -1180,7 +1180,7 @@ test_wpa_ap_leap_connection_2(gconstpointer data)
     fill_wsec(src, src_wsec);
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   wpa_flags_for_idx(idx),
                                   rsn_flags_for_idx(idx),
@@ -1214,7 +1214,7 @@ test_wpa_ap_dynamic_wep_connection(gconstpointer data)
     fill_wsec(src, src_wsec);
     success = complete_connection("blahblah",
                                   bssid,
-                                  NM_802_11_MODE_INFRA,
+                                  _NM_802_11_MODE_INFRA,
                                   NM_802_11_AP_FLAGS_PRIVACY,
                                   wpa_flags_for_idx(idx),
                                   rsn_flags_for_idx(idx),
diff --git a/src/core/devices/wwan/meson.build b/src/core/devices/wwan/meson.build
index 87af0429..37ef738c 100644
--- a/src/core/devices/wwan/meson.build
+++ b/src/core/devices/wwan/meson.build
@@ -17,7 +17,6 @@ libnm_wwan = shared_module(
     libsystemd_dep,
     mm_glib_dep,
   ],
-  c_args: daemon_c_flags,
   link_args: '-Wl,--version-script,@0@'.format(linker_script),
   link_depends: linker_script,
   install: true,
@@ -51,7 +50,6 @@ libnm_device_plugin_wwan = shared_module(
     libsystemd_dep,
     mm_glib_dep,
   ],
-  c_args: daemon_c_flags,
   link_with: libnm_wwan,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c
index 3ea89d2c..a8d2a161 100644
--- a/src/core/devices/wwan/nm-device-modem.c
+++ b/src/core/devices/wwan/nm-device-modem.c
@@ -14,7 +14,7 @@
 #include "settings/nm-settings-connection.h"
 #include "nm-modem-broadband.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceModem
 #include "devices/nm-device-logging.h"
@@ -357,7 +357,6 @@ modem_state_cb(NMModem *modem, int new_state_i, int old_state_i, gpointer user_d
         nm_device_state_changed(device,
                                 NM_DEVICE_STATE_FAILED,
                                 NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
-        return;
     }
 
     if (new_state > NM_MODEM_STATE_LOCKED && old_state == NM_MODEM_STATE_LOCKED) {
@@ -856,14 +855,13 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_device_modem = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DEVICE_MODEM,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ModemCapabilities",
-                                                             "u",
-                                                             NM_DEVICE_MODEM_CAPABILITIES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("CurrentCapabilities",
-                                                             "u",
-                                                             NM_DEVICE_MODEM_CURRENT_CAPABILITIES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ModemCapabilities",
+                                                           "u",
+                                                           NM_DEVICE_MODEM_CAPABILITIES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("CurrentCapabilities",
+                                                           "u",
+                                                           NM_DEVICE_MODEM_CURRENT_CAPABILITIES),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DeviceId",
                                                            "s",
                                                            NM_DEVICE_MODEM_DEVICE_ID),
@@ -871,7 +869,6 @@ static const NMDBusInterfaceInfoExtended interface_info_device_modem = {
                                                            "s",
                                                            NM_DEVICE_MODEM_OPERATOR_CODE),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Apn", "s", NM_DEVICE_MODEM_APN), ), ),
-    .legacy_property_changed = TRUE,
 };
 
 static void
diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c
index ca028804..0872a8a1 100644
--- a/src/core/devices/wwan/nm-modem-broadband.c
+++ b/src/core/devices/wwan/nm-modem-broadband.c
@@ -11,10 +11,10 @@
 #include <arpa/inet.h>
 #include <libmm-glib.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 
@@ -369,10 +369,10 @@ static void
 connect_ready(MMModemSimple *simple_iface, GAsyncResult *res, NMModemBroadband *self)
 {
     ConnectContext *ctx;
-    GError *        error      = NULL;
-    NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
-    NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
-    MMBearer *      bearer;
+    GError *        error            = NULL;
+    NMModemIPMethod ip4_method       = NM_MODEM_IP_METHOD_UNKNOWN;
+    NMModemIPMethod ip6_method       = NM_MODEM_IP_METHOD_UNKNOWN;
+    gs_unref_object MMBearer *bearer = NULL;
 
     bearer = mm_modem_simple_connect_finish(simple_iface, res, &error);
 
@@ -386,7 +386,7 @@ connect_ready(MMModemSimple *simple_iface, GAsyncResult *res, NMModemBroadband *
     if (!ctx)
         return;
 
-    self->_priv.bearer = bearer;
+    self->_priv.bearer = g_steal_pointer(&bearer);
 
     if (!self->_priv.bearer) {
         if (g_error_matches(error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)
diff --git a/src/core/devices/wwan/nm-modem-manager.c b/src/core/devices/wwan/nm-modem-manager.c
index 598c6898..4fb9a378 100644
--- a/src/core/devices/wwan/nm-modem-manager.c
+++ b/src/core/devices/wwan/nm-modem-manager.c
@@ -17,7 +17,7 @@
     #define sd_booted() FALSE
 #endif
 
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 #include "nm-modem.h"
 #include "nm-modem-broadband.h"
 
diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c
index 21734cee..78ad7b68 100644
--- a/src/core/devices/wwan/nm-modem-ofono.c
+++ b/src/core/devices/wwan/nm-modem-ofono.c
@@ -7,10 +7,10 @@
 
 #include "nm-modem-ofono.h"
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "devices/nm-device-private.h"
 #include "nm-modem.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-ip4-config.h"
 
 #define VARIANT_IS_OF_TYPE_BOOLEAN(v) \
@@ -316,10 +316,9 @@ handle_sim_property(GDBusProxy *proxy, const char *property, GVariant *v, gpoint
 static void
 sim_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data)
 {
-    GVariant *v_child = g_variant_get_child_value(v, 0);
+    gs_unref_variant GVariant *v_child = g_variant_get_child_value(v, 0);
 
     handle_sim_property(proxy, property, v_child, user_data);
-    g_variant_unref(v_child);
 }
 
 static void
@@ -330,7 +329,7 @@ sim_get_properties_done(GObject *source, GAsyncResult *result, gpointer user_dat
     gs_free_error GError *error             = NULL;
     gs_unref_variant GVariant *v_properties = NULL;
     gs_unref_variant GVariant *v_dict       = NULL;
-    GVariant *                 v;
+    gs_unref_variant GVariant *v            = NULL;
     GVariantIter               i;
     const char *               property;
 
@@ -370,9 +369,8 @@ sim_get_properties_done(GObject *source, GAsyncResult *result, gpointer user_dat
      */
 
     g_variant_iter_init(&i, v_dict);
-    while (g_variant_iter_next(&i, "{&sv}", &property, &v)) {
+    while (g_variant_iter_loop(&i, "{&sv}", &property, &v)) {
         handle_sim_property(NULL, property, v, self);
-        g_variant_unref(v);
     }
 }
 
@@ -477,10 +475,9 @@ handle_connman_property(GDBusProxy *proxy, const char *property, GVariant *v, gp
 static void
 connman_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data)
 {
-    GVariant *v_child = g_variant_get_child_value(v, 0);
+    gs_unref_variant GVariant *v_child = g_variant_get_child_value(v, 0);
 
     handle_connman_property(proxy, property, v_child, user_data);
-    g_variant_unref(v_child);
 }
 
 static void
@@ -491,7 +488,7 @@ connman_get_properties_done(GObject *source, GAsyncResult *result, gpointer user
     gs_free_error GError *error             = NULL;
     gs_unref_variant GVariant *v_properties = NULL;
     gs_unref_variant GVariant *v_dict       = NULL;
-    GVariant *                 v;
+    gs_unref_variant GVariant *v            = NULL;
     GVariantIter               i;
     const char *               property;
 
@@ -523,9 +520,8 @@ connman_get_properties_done(GObject *source, GAsyncResult *result, gpointer user
      */
 
     g_variant_iter_init(&i, v_dict);
-    while (g_variant_iter_next(&i, "{&sv}", &property, &v)) {
+    while (g_variant_iter_loop(&i, "{&sv}", &property, &v)) {
         handle_connman_property(NULL, property, v, self);
-        g_variant_unref(v);
     }
 }
 
@@ -700,9 +696,8 @@ modem_get_properties_done(GObject *source, GAsyncResult *result, gpointer user_d
      */
 
     g_variant_iter_init(&i, v_dict);
-    while (g_variant_iter_next(&i, "{&sv}", &property, &v)) {
+    while (g_variant_iter_loop(&i, "{&sv}", &property, &v)) {
         handle_modem_property(NULL, property, v, self);
-        g_variant_unref(v);
     }
 }
 
@@ -726,35 +721,30 @@ stage1_prepare_done(GObject *source, GAsyncResult *result, gpointer user_data)
     nm_clear_pointer(&priv->connect_properties, g_hash_table_destroy);
 
     if (error) {
-        _LOGW("connection failed: %s", error->message);
-
-        nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, NM_DEVICE_STATE_REASON_MODEM_BUSY);
-        /*
-         * FIXME: add code to check for InProgress so that the
-         * connection doesn't continue to try and activate,
-         * leading to the connection being disabled, and a 5m
-         * timeout...
-         */
+        if (!g_strstr_len(error->message,
+                          NM_STRLEN(OFONO_ERROR_IN_PROGRESS),
+                          OFONO_ERROR_IN_PROGRESS)) {
+            nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, NM_DEVICE_STATE_REASON_MODEM_BUSY);
+        }
     }
 }
 
 static void
-context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data)
+handle_settings(GVariant *v_dict, gpointer user_data)
 {
     NMModemOfono *       self = NM_MODEM_OFONO(user_data);
     NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self);
     NMPlatformIP4Address addr;
-    gboolean             ret          = FALSE;
-    gs_unref_variant GVariant *v_dict = NULL;
-    const char *               interface;
-    const char *               s;
-    const char **              array, **iter;
-    guint32                    address_network, gateway_network;
-    guint32                    ip4_route_table, ip4_route_metric;
-    int                        ifindex;
-    GError *                   error = NULL;
+    gboolean             ret = FALSE;
+    const char *         interface;
+    const char *         s;
+    const char **        array, **iter;
+    guint32              address_network, gateway_network;
+    guint32              ip4_route_table, ip4_route_metric;
+    int                  ifindex;
+    GError *             error = NULL;
 
-    _LOGD("PropertyChanged: %s", property);
+    //_LOGD("PropertyChanged: %s", property);
 
     /*
      * TODO: might be a good idea and re-factor this to mimic bluez-device,
@@ -762,15 +752,6 @@ context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, g
      * handle the action.
      */
 
-    if (g_strcmp0(property, "Settings") != 0)
-        return;
-
-    v_dict = g_variant_get_child_value(v, 0);
-    if (!v_dict) {
-        _LOGW("error getting IPv4 Settings: no v_dict");
-        goto out;
-    }
-
     _LOGI("IPv4 static Settings:");
 
     if (!g_variant_lookup(v_dict, "Interface", "&s", &interface)) {
@@ -909,6 +890,28 @@ out:
     }
 }
 
+static void
+context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data)
+{
+    NMModemOfono *   self             = NM_MODEM_OFONO(user_data);
+    gs_unref_variant GVariant *v_dict = NULL;
+
+    _LOGD("PropertyChanged: %s", property);
+
+    if (g_strcmp0(property, "Settings") != 0)
+        return;
+
+    v_dict = g_variant_get_child_value(v, 0);
+    if (!v_dict) {
+        _LOGW("ofono: (%s): error getting IPv4 Settings", nm_modem_get_uid(NM_MODEM(self)));
+        return;
+    }
+
+    g_assert(g_variant_is_of_type(v_dict, G_VARIANT_TYPE_VARDICT));
+
+    handle_settings(v_dict, user_data);
+}
+
 static NMActStageReturn
 static_stage3_ip4_config_start(NMModem *            modem,
                                NMActRequest *       req,
@@ -936,6 +939,72 @@ static_stage3_ip4_config_start(NMModem *            modem,
 }
 
 static void
+context_properties_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+    NMModemOfono *       self;
+    NMModemOfonoPrivate *priv;
+    gs_free_error GError *error           = NULL;
+    gs_unref_variant GVariant *properties = NULL;
+    gs_unref_variant GVariant *settings   = NULL;
+    gs_unref_variant GVariant *v_dict     = NULL;
+    gboolean                   active;
+
+    self = NM_MODEM_OFONO(user_data);
+    priv = NM_MODEM_OFONO_GET_PRIVATE(self);
+
+    properties = g_dbus_proxy_call_finish(proxy, result, &error);
+
+    if (!properties) {
+        _LOGW("ofono: connection failed: no context properties returned %s", error->message);
+        g_clear_error(&error);
+        goto error;
+    }
+
+    v_dict = g_variant_get_child_value(properties, 0);
+    if (!v_dict || !g_variant_is_of_type(v_dict, G_VARIANT_TYPE_VARDICT)) {
+        _LOGW("ofono: connection failed; could not read connection properties");
+        goto error;
+    }
+
+    if (!g_variant_lookup(v_dict, "Active", "b", &active)) {
+        _LOGW("ofono: connection failed; can not read 'Active' property");
+        goto error;
+    }
+
+    /* Watch for custom ofono PropertyChanged signals */
+    _nm_dbus_signal_connect(priv->context_proxy,
+                            "PropertyChanged",
+                            G_VARIANT_TYPE("(sv)"),
+                            G_CALLBACK(context_property_changed),
+                            self);
+
+    if (active) {
+        _LOGD("ofono: connection is already Active");
+
+        settings = g_variant_lookup_value(v_dict, "Settings", G_VARIANT_TYPE_VARDICT);
+        if (settings == NULL) {
+            _LOGW("ofono: connection failed; can not read 'Settings' property");
+            goto error;
+        }
+
+        handle_settings(settings, user_data);
+    } else {
+        g_dbus_proxy_call(priv->context_proxy,
+                          "SetProperty",
+                          g_variant_new("(sv)", "Active", g_variant_new("b", TRUE)),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          20000,
+                          NULL,
+                          (GAsyncReadyCallback) stage1_prepare_done,
+                          self);
+    }
+    return;
+
+error:
+    nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, NM_DEVICE_STATE_REASON_MODEM_BUSY);
+}
+
+static void
 context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
     NMModemOfono *       self;
@@ -973,19 +1042,15 @@ context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data)
      */
     g_clear_object(&priv->ip4_config);
 
-    _nm_dbus_signal_connect(priv->context_proxy,
-                            "PropertyChanged",
-                            G_VARIANT_TYPE("(sv)"),
-                            G_CALLBACK(context_property_changed),
-                            self);
-
+    /* We need to directly query ConnectionContextinteface to get the current
+     * property values */
     g_dbus_proxy_call(priv->context_proxy,
-                      "SetProperty",
-                      g_variant_new("(sv)", "Active", g_variant_new("b", TRUE)),
+                      "GetProperties",
+                      NULL,
                       G_DBUS_CALL_FLAGS_NONE,
                       20000,
-                      priv->context_proxy_cancellable,
-                      stage1_prepare_done,
+                      NULL,
+                      (GAsyncReadyCallback) context_properties_cb,
                       self);
 }
 
@@ -1069,6 +1134,7 @@ modem_act_stage1_prepare(NMModem *            modem,
 
     _LOGI("activating context %s", priv->context_path);
 
+    update_modem_state(self);
     if (nm_modem_get_state(modem) == NM_MODEM_STATE_REGISTERED) {
         do_context_activate(self);
     } else {
diff --git a/src/core/devices/wwan/nm-modem-ofono.h b/src/core/devices/wwan/nm-modem-ofono.h
index 260e3954..ebe21023 100644
--- a/src/core/devices/wwan/nm-modem-ofono.h
+++ b/src/core/devices/wwan/nm-modem-ofono.h
@@ -25,6 +25,8 @@
 #define OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT "org.ofono.ConnectionContext"
 #define OFONO_DBUS_INTERFACE_SIM_MANAGER        "org.ofono.SimManager"
 
+#define OFONO_ERROR_IN_PROGRESS "org.ofono.Error.InProgress"
+
 typedef struct _NMModemOfono      NMModemOfono;
 typedef struct _NMModemOfonoClass NMModemOfonoClass;
 
diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c
index 0d334fa4..15baa677 100644
--- a/src/core/devices/wwan/nm-modem.c
+++ b/src/core/devices/wwan/nm-modem.c
@@ -10,10 +10,11 @@
 
 #include <fcntl.h>
 #include <termios.h>
+#include <linux/if.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-setting-connection.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
@@ -799,8 +800,10 @@ nm_modem_ip4_pre_commit(NMModem *modem, NMDevice *device, NMIP4Config *config)
 
         g_assert(address);
         if (address->plen == 32)
-            nm_platform_link_set_noarp(nm_device_get_platform(device),
-                                       nm_device_get_ip_ifindex(device));
+            nm_platform_link_change_flags(nm_device_get_platform(device),
+                                          nm_device_get_ip_ifindex(device),
+                                          IFF_NOARP,
+                                          TRUE);
     }
 }
 
@@ -1199,7 +1202,7 @@ deactivate_cleanup(NMModem *self, NMDevice *device, gboolean stop_ppp_manager)
 
                 nm_platform_ip_route_flush(platform, AF_UNSPEC, ifindex);
                 nm_platform_ip_address_flush(platform, AF_UNSPEC, ifindex);
-                nm_platform_link_set_down(platform, ifindex);
+                nm_platform_link_change_flags(platform, ifindex, IFF_UP, FALSE);
             }
         }
     }
diff --git a/src/core/devices/wwan/nm-wwan-factory.c b/src/core/devices/wwan/nm-wwan-factory.c
index 5d2ce2b3..0d802ae1 100644
--- a/src/core/devices/wwan/nm-wwan-factory.c
+++ b/src/core/devices/wwan/nm-wwan-factory.c
@@ -12,7 +12,7 @@
 #include "nm-setting-cdma.h"
 #include "nm-modem-manager.h"
 #include "nm-device-modem.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/dhcp/meson.build b/src/core/dhcp/meson.build
index 1bb004cd..ddaa32fb 100644
--- a/src/core/dhcp/meson.build
+++ b/src/core/dhcp/meson.build
@@ -3,10 +3,11 @@
 executable(
   'nm-dhcp-helper',
   'nm-dhcp-helper.c',
-  dependencies: glib_nm_default_dep,
-  c_args: [
-    '-DG_LOG_DOMAIN="nm-dhcp-helper"',
+  include_directories: [
+    src_inc,
+    top_inc,
   ],
+  dependencies: glib_dep,
   link_args: ldflags_linker_script_binary,
   link_depends: linker_script_binary,
   install: true,
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
index c38c814e..ab8243d0 100644
--- a/src/core/dhcp/nm-dhcp-client.c
+++ b/src/core/dhcp/nm-dhcp-client.c
@@ -13,15 +13,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <linux/rtnetlink.h>
+#include <linux/if_ether.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-glib-aux/nm-random-utils.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-random-utils.h"
 
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-dhcp-utils.h"
 #include "nm-dhcp-options.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #include "nm-dhcp-client-logging.h"
 
@@ -33,6 +34,7 @@ static guint signals[LAST_SIGNAL] = {0};
 
 NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient,
                              PROP_ADDR_FAMILY,
+                             PROP_ANYCAST_ADDRESS,
                              PROP_FLAGS,
                              PROP_HWADDR,
                              PROP_BROADCAST_HWADDR,
@@ -61,6 +63,7 @@ typedef struct _NMDhcpClientPrivate {
     char *              hostname;
     const char **       reject_servers;
     char *              mud_url;
+    char *              anycast_address;
     GBytes *            vendor_class_identifier;
     pid_t               pid;
     guint               timeout_id;
@@ -73,8 +76,7 @@ typedef struct _NMDhcpClientPrivate {
     guint32             iaid;
     NMDhcpState         state;
     NMDhcpHostnameFlags hostname_flags;
-    bool                info_only : 1;
-    bool                use_fqdn : 1;
+    NMDhcpClientFlags   client_flags;
     bool                iaid_explicit : 1;
 } NMDhcpClientPrivate;
 
@@ -84,6 +86,11 @@ G_DEFINE_ABSTRACT_TYPE(NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+/* we use pid=-1 for invalid PIDs. Ensure that pid_t can hold negative values. */
+G_STATIC_ASSERT(!(((pid_t) -1) > 0));
+
+/*****************************************************************************/
+
 pid_t
 nm_dhcp_client_get_pid(NMDhcpClient *self)
 {
@@ -277,6 +284,14 @@ nm_dhcp_client_set_client_id_bin(NMDhcpClient *self,
 }
 
 const char *
+nm_dhcp_client_get_anycast_address(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->anycast_address;
+}
+
+const char *
 nm_dhcp_client_get_hostname(NMDhcpClient *self)
 {
     g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
@@ -292,20 +307,12 @@ nm_dhcp_client_get_hostname_flags(NMDhcpClient *self)
     return NM_DHCP_CLIENT_GET_PRIVATE(self)->hostname_flags;
 }
 
-gboolean
-nm_dhcp_client_get_info_only(NMDhcpClient *self)
+NMDhcpClientFlags
+nm_dhcp_client_get_client_flags(NMDhcpClient *self)
 {
-    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NM_DHCP_CLIENT_FLAGS_NONE);
 
-    return NM_DHCP_CLIENT_GET_PRIVATE(self)->info_only;
-}
-
-gboolean
-nm_dhcp_client_get_use_fqdn(NMDhcpClient *self)
-{
-    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
-
-    return NM_DHCP_CLIENT_GET_PRIVATE(self)->use_fqdn;
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->client_flags;
 }
 
 const char *
@@ -334,29 +341,25 @@ nm_dhcp_client_get_reject_servers(NMDhcpClient *self)
 
 /*****************************************************************************/
 
-static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
-    [NM_DHCP_STATE_UNKNOWN]    = "unknown",
-    [NM_DHCP_STATE_BOUND]      = "bound",
-    [NM_DHCP_STATE_EXTENDED]   = "extended",
-    [NM_DHCP_STATE_TIMEOUT]    = "timeout",
-    [NM_DHCP_STATE_EXPIRE]     = "expire",
-    [NM_DHCP_STATE_DONE]       = "done",
-    [NM_DHCP_STATE_FAIL]       = "fail",
-    [NM_DHCP_STATE_TERMINATED] = "terminated",
-};
-
-static const char *
-state_to_string(NMDhcpState state)
-{
-    if ((gsize) state < G_N_ELEMENTS(state_table))
-        return state_table[state];
-    return NULL;
-}
+NM_UTILS_LOOKUP_STR_DEFINE(nm_dhcp_state_to_string,
+                           NMDhcpState,
+                           NM_UTILS_LOOKUP_DEFAULT(NULL),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_BOUND, "bound"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_DONE, "done"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_EXPIRE, "expire"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_EXTENDED, "extended"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_FAIL, "fail"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_NOOP, "noop"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_TERMINATED, "terminated"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_TIMEOUT, "timeout"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_UNKNOWN, "unknown"),
+                           NM_UTILS_LOOKUP_ITEM_IGNORE(__NM_DHCP_STATE_MAX), );
 
 static NMDhcpState
 reason_to_state(NMDhcpClient *self, const char *iface, const char *reason)
 {
-    if (g_ascii_strcasecmp(reason, "bound") == 0 || g_ascii_strcasecmp(reason, "bound6") == 0)
+    if (g_ascii_strcasecmp(reason, "bound") == 0 || g_ascii_strcasecmp(reason, "bound6") == 0
+        || g_ascii_strcasecmp(reason, "static") == 0)
         return NM_DHCP_STATE_BOUND;
     else if (g_ascii_strcasecmp(reason, "renew") == 0 || g_ascii_strcasecmp(reason, "renew6") == 0
              || g_ascii_strcasecmp(reason, "reboot") == 0
@@ -488,8 +491,8 @@ nm_dhcp_client_set_state(NMDhcpClient *self,
         const char *addr = nm_g_hash_table_lookup(options, req_str);
 
         _LOGI("state changed %s -> %s%s%s%s",
-              state_to_string(priv->state),
-              state_to_string(new_state),
+              nm_dhcp_state_to_string(priv->state),
+              nm_dhcp_state_to_string(new_state),
               NM_PRINT_FMT_QUOTED(addr, ", address=", addr, "", ""));
     }
 
@@ -514,20 +517,12 @@ daemon_watch_cb(GPid pid, int status, gpointer user_data)
 {
     NMDhcpClient *       self = NM_DHCP_CLIENT(user_data);
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+    gs_free char *       desc = NULL;
 
     g_return_if_fail(priv->watch_id);
     priv->watch_id = 0;
 
-    if (WIFEXITED(status))
-        _LOGI("client pid %d exited with status %d", pid, WEXITSTATUS(status));
-    else if (WIFSIGNALED(status))
-        _LOGI("client pid %d killed by signal %d", pid, WTERMSIG(status));
-    else if (WIFSTOPPED(status))
-        _LOGI("client pid %d stopped by signal %d", pid, WSTOPSIG(status));
-    else if (WIFCONTINUED(status))
-        _LOGI("client pid %d resumed (by SIGCONT)", pid);
-    else
-        _LOGW("client died abnormally");
+    _LOGI("client pid %d %s", pid, (desc = nm_utils_get_process_exit_status_desc(status)));
 
     priv->pid = -1;
 
@@ -578,7 +573,6 @@ nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid)
 gboolean
 nm_dhcp_client_start_ip4(NMDhcpClient *self,
                          GBytes *      client_id,
-                         const char *  dhcp_anycast_addr,
                          const char *  last_ip4_address,
                          GError **     error)
 {
@@ -598,10 +592,7 @@ nm_dhcp_client_start_ip4(NMDhcpClient *self,
 
     nm_dhcp_client_set_client_id(self, client_id);
 
-    return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self,
-                                                     dhcp_anycast_addr,
-                                                     last_ip4_address,
-                                                     error);
+    return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, last_ip4_address, error);
 }
 
 gboolean
@@ -638,7 +629,6 @@ gboolean
 nm_dhcp_client_start_ip6(NMDhcpClient *            self,
                          GBytes *                  client_id,
                          gboolean                  enforce_duid,
-                         const char *              dhcp_anycast_addr,
                          const struct in6_addr *   ll_addr,
                          NMSettingIP6ConfigPrivacy privacy,
                          guint                     needed_prefixes,
@@ -667,8 +657,11 @@ nm_dhcp_client_start_ip6(NMDhcpClient *            self,
     else
         _LOGI("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout);
 
-    return NM_DHCP_CLIENT_GET_CLASS(self)
-        ->ip6_start(self, dhcp_anycast_addr, ll_addr, privacy, needed_prefixes, error);
+    return NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self,
+                                                     ll_addr,
+                                                     privacy,
+                                                     needed_prefixes,
+                                                     error);
 }
 
 void
@@ -749,7 +742,7 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release)
         _LOGI("canceled DHCP transaction");
     nm_assert(priv->pid == -1);
 
-    nm_dhcp_client_set_state(self, NM_DHCP_STATE_DONE, NULL, NULL);
+    nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL, NULL);
 }
 
 /*****************************************************************************/
@@ -898,8 +891,8 @@ nm_dhcp_client_handle_event(gpointer      unused,
     old_state = priv->state;
     new_state = reason_to_state(self, priv->iface, reason);
     _LOGD("DHCP state '%s' -> '%s' (reason: '%s')",
-          state_to_string(old_state),
-          state_to_string(new_state),
+          nm_dhcp_state_to_string(old_state),
+          nm_dhcp_state_to_string(new_state),
           reason);
 
     if (new_state == NM_DHCP_STATE_NOOP)
@@ -930,12 +923,12 @@ nm_dhcp_client_handle_event(gpointer      unused,
                                                           priv->route_metric));
             } else {
                 prefix    = nm_dhcp_utils_ip6_prefix_from_options(str_options);
-                ip_config = NM_IP_CONFIG_CAST(
-                    nm_dhcp_utils_ip6_config_from_options(nm_dhcp_client_get_multi_idx(self),
-                                                          priv->ifindex,
-                                                          priv->iface,
-                                                          str_options,
-                                                          priv->info_only));
+                ip_config = NM_IP_CONFIG_CAST(nm_dhcp_utils_ip6_config_from_options(
+                    nm_dhcp_client_get_multi_idx(self),
+                    priv->ifindex,
+                    priv->iface,
+                    str_options,
+                    NM_FLAGS_HAS(priv->client_flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY)));
             }
         } else
             g_warn_if_reached();
@@ -1052,11 +1045,8 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     case PROP_FLAGS:
         /* construct-only */
         flags = g_value_get_uint(value);
-        nm_assert(
-            (flags & ~((guint)(NM_DHCP_CLIENT_FLAGS_INFO_ONLY | NM_DHCP_CLIENT_FLAGS_USE_FQDN)))
-            == 0);
-        priv->info_only = NM_FLAGS_HAS(flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY);
-        priv->use_fqdn  = NM_FLAGS_HAS(flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN);
+        nm_assert(!NM_FLAGS_ANY(flags, ~((guint) NM_DHCP_CLIENT_FLAGS_ALL)));
+        priv->client_flags = flags;
         break;
     case PROP_MULTI_IDX:
         /* construct-only */
@@ -1080,6 +1070,10 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         /* construct-only */
         priv->hwaddr = g_value_dup_boxed(value);
         break;
+    case PROP_ANYCAST_ADDRESS:
+        /* construct-only */
+        priv->anycast_address = g_value_dup_string(value);
+        break;
     case PROP_BROADCAST_HWADDR:
         /* construct-only */
         priv->bcast_hwaddr = g_value_dup_boxed(value);
@@ -1153,6 +1147,28 @@ nm_dhcp_client_init(NMDhcpClient *self)
     priv->pid = -1;
 }
 
+#if NM_MORE_ASSERTS
+static void
+constructed(GObject *object)
+{
+    NMDhcpClient *       self = NM_DHCP_CLIENT(object);
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    /* certain flags only make sense with certain address family. Assert
+     * for that. */
+    if (NM_IS_IPv4(priv->addr_family))
+        nm_assert(!NM_FLAGS_ANY(priv->client_flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY));
+    else {
+        nm_assert(NM_FLAGS_HAS(priv->client_flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN));
+        nm_assert(!NM_FLAGS_ANY(priv->client_flags, NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST));
+    }
+
+    nm_assert(!priv->anycast_address || nm_utils_hwaddr_valid(priv->anycast_address, ETH_ALEN));
+
+    G_OBJECT_CLASS(nm_dhcp_client_parent_class)->constructed(object);
+}
+#endif
+
 static void
 dispose(GObject *object)
 {
@@ -1172,6 +1188,7 @@ dispose(GObject *object)
     nm_clear_g_free(&priv->iface);
     nm_clear_g_free(&priv->hostname);
     nm_clear_g_free(&priv->uuid);
+    nm_clear_g_free(&priv->anycast_address);
     nm_clear_g_free(&priv->mud_url);
     nm_clear_g_free(&priv->reject_servers);
     nm_clear_pointer(&priv->client_id, g_bytes_unref);
@@ -1191,6 +1208,9 @@ nm_dhcp_client_class_init(NMDhcpClientClass *client_class)
 
     g_type_class_add_private(client_class, sizeof(NMDhcpClientPrivate));
 
+#if NM_MORE_ASSERTS
+    object_class->constructed = constructed;
+#endif
     object_class->dispose      = dispose;
     object_class->get_property = get_property;
     object_class->set_property = set_property;
@@ -1243,6 +1263,13 @@ nm_dhcp_client_class_init(NMDhcpClientClass *client_class)
                          AF_UNSPEC,
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
+    obj_properties[PROP_ANYCAST_ADDRESS] =
+        g_param_spec_string(NM_DHCP_CLIENT_ANYCAST_ADDRESS,
+                            "",
+                            "",
+                            NULL,
+                            G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
     obj_properties[PROP_UUID] =
         g_param_spec_string(NM_DHCP_CLIENT_UUID,
                             "",
diff --git a/src/core/dhcp/nm-dhcp-client.h b/src/core/dhcp/nm-dhcp-client.h
index 72ab477d..3fe1b34e 100644
--- a/src/core/dhcp/nm-dhcp-client.h
+++ b/src/core/dhcp/nm-dhcp-client.h
@@ -25,6 +25,7 @@
     (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
 
 #define NM_DHCP_CLIENT_ADDR_FAMILY             "addr-family"
+#define NM_DHCP_CLIENT_ANYCAST_ADDRESS         "anycast-address"
 #define NM_DHCP_CLIENT_FLAGS                   "flags"
 #define NM_DHCP_CLIENT_HWADDR                  "hwaddr"
 #define NM_DHCP_CLIENT_BROADCAST_HWADDR        "broadcast-hwaddr"
@@ -51,7 +52,7 @@ typedef enum {
     NM_DHCP_STATE_BOUND,      /* new lease */
     NM_DHCP_STATE_EXTENDED,   /* lease extended */
     NM_DHCP_STATE_TIMEOUT,    /* timed out contacting server */
-    NM_DHCP_STATE_DONE,       /* client quit or stopped */
+    NM_DHCP_STATE_DONE,       /* client reported it's stopping */
     NM_DHCP_STATE_EXPIRE,     /* lease expired or NAKed */
     NM_DHCP_STATE_FAIL,       /* failed for some reason */
     NM_DHCP_STATE_TERMINATED, /* client is no longer running */
@@ -60,6 +61,8 @@ typedef enum {
     NM_DHCP_STATE_MAX = __NM_DHCP_STATE_MAX - 1,
 } NMDhcpState;
 
+const char *nm_dhcp_state_to_string(NMDhcpState state);
+
 struct _NMDhcpClientPrivate;
 
 typedef struct {
@@ -68,25 +71,27 @@ typedef struct {
     CList                        dhcp_client_lst;
 } NMDhcpClient;
 
-typedef enum {
-    NM_DHCP_CLIENT_FLAGS_INFO_ONLY = (1LL << 0),
-    NM_DHCP_CLIENT_FLAGS_USE_FQDN  = (1LL << 1),
+typedef enum _nm_packed {
+    NM_DHCP_CLIENT_FLAGS_NONE = 0,
+
+    NM_DHCP_CLIENT_FLAGS_INFO_ONLY         = (1LL << 0),
+    NM_DHCP_CLIENT_FLAGS_USE_FQDN          = (1LL << 1),
+    NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST = (1LL << 2),
+
+    _NM_DHCP_CLIENT_FLAGS_LAST,
+    NM_DHCP_CLIENT_FLAGS_ALL = ((_NM_DHCP_CLIENT_FLAGS_LAST - 1) << 1) - 1,
 } NMDhcpClientFlags;
 
 typedef struct {
     GObjectClass parent;
 
-    gboolean (*ip4_start)(NMDhcpClient *self,
-                          const char *  anycast_addr,
-                          const char *  last_ip4_address,
-                          GError **     error);
+    gboolean (*ip4_start)(NMDhcpClient *self, const char *last_ip4_address, GError **error);
 
     gboolean (*accept)(NMDhcpClient *self, GError **error);
 
     gboolean (*decline)(NMDhcpClient *self, const char *error_message, GError **error);
 
     gboolean (*ip6_start)(NMDhcpClient *            self,
-                          const char *              anycast_addr,
                           const struct in6_addr *   ll_addr,
                           NMSettingIP6ConfigPrivacy privacy,
                           guint                     needed_prefixes,
@@ -126,6 +131,8 @@ GBytes *nm_dhcp_client_get_hw_addr(NMDhcpClient *self);
 
 GBytes *nm_dhcp_client_get_broadcast_hw_addr(NMDhcpClient *self);
 
+const char *nm_dhcp_client_get_anycast_address(NMDhcpClient *self);
+
 guint32 nm_dhcp_client_get_route_table(NMDhcpClient *self);
 
 void nm_dhcp_client_set_route_table(NMDhcpClient *self, guint32 route_table);
@@ -148,22 +155,18 @@ const char *const *nm_dhcp_client_get_reject_servers(NMDhcpClient *self);
 
 NMDhcpHostnameFlags nm_dhcp_client_get_hostname_flags(NMDhcpClient *self);
 
-gboolean nm_dhcp_client_get_info_only(NMDhcpClient *self);
-
-gboolean nm_dhcp_client_get_use_fqdn(NMDhcpClient *self);
+NMDhcpClientFlags nm_dhcp_client_get_client_flags(NMDhcpClient *self);
 
 GBytes *nm_dhcp_client_get_vendor_class_identifier(NMDhcpClient *self);
 
 gboolean nm_dhcp_client_start_ip4(NMDhcpClient *self,
                                   GBytes *      client_id,
-                                  const char *  dhcp_anycast_addr,
                                   const char *  last_ip4_address,
                                   GError **     error);
 
 gboolean nm_dhcp_client_start_ip6(NMDhcpClient *            self,
                                   GBytes *                  client_id,
                                   gboolean                  enforce_duid,
-                                  const char *              dhcp_anycast_addr,
                                   const struct in6_addr *   ll_addr,
                                   NMSettingIP6ConfigPrivacy privacy,
                                   guint                     needed_prefixes,
diff --git a/src/core/dhcp/nm-dhcp-dhclient-utils.c b/src/core/dhcp/nm-dhcp-dhclient-utils.c
index ad1e097f..4284a852 100644
--- a/src/core/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/core/dhcp/nm-dhcp-dhclient-utils.c
@@ -12,12 +12,12 @@
 #include <net/if.h>
 #include <linux/if_ether.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
 
 #include "nm-dhcp-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-utils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "NetworkManagerUtils.h"
 
 #define TIMEOUT_TAG  "timeout "
@@ -291,7 +291,7 @@ char *
 nm_dhcp_dhclient_create_config(const char *        interface,
                                int                 addr_family,
                                GBytes *            client_id,
-                               const char *        anycast_addr,
+                               const char *        anycast_address,
                                const char *        hostname,
                                guint32             timeout,
                                gboolean            use_fqdn,
@@ -308,7 +308,8 @@ nm_dhcp_dhclient_create_config(const char *        interface,
     gboolean                     reset_reqlist = FALSE;
     int                          i;
 
-    g_return_val_if_fail(!anycast_addr || nm_utils_hwaddr_valid(anycast_addr, ETH_ALEN), NULL);
+    g_return_val_if_fail(!anycast_address || nm_utils_hwaddr_valid(anycast_address, ETH_ALEN),
+                         NULL);
     g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), NULL);
     g_return_val_if_fail(!reject_servers || addr_family == AF_INET, NULL);
     nm_assert(!out_new_client_id || !*out_new_client_id);
@@ -508,14 +509,14 @@ nm_dhcp_dhclient_create_config(const char *        interface,
 
     g_string_append_c(new_contents, '\n');
 
-    if (anycast_addr) {
+    if (anycast_address) {
         g_string_append_printf(new_contents,
                                "interface \"%s\" {\n"
                                " initial-interval 1; \n"
                                " anycast-mac ethernet %s;\n"
                                "}\n",
                                interface,
-                               anycast_addr);
+                               anycast_address);
     }
 
     return g_string_free(g_steal_pointer(&new_contents), FALSE);
diff --git a/src/core/dhcp/nm-dhcp-dhclient.c b/src/core/dhcp/nm-dhcp-dhclient.c
index c42a0ba5..4a11250f 100644
--- a/src/core/dhcp/nm-dhcp-dhclient.c
+++ b/src/core/dhcp/nm-dhcp-dhclient.c
@@ -21,7 +21,7 @@
     #include <arpa/inet.h>
     #include <ctype.h>
 
-    #include "nm-glib-aux/nm-dedup-multi.h"
+    #include "libnm-glib-aux/nm-dedup-multi.h"
 
     #include "nm-utils.h"
     #include "nm-dhcp-dhclient-utils.h"
@@ -151,7 +151,7 @@ merge_dhclient_config(NMDhcpDhclient *    self,
                       const char *        iface,
                       const char *        conf_file,
                       GBytes *            client_id,
-                      const char *        anycast_addr,
+                      const char *        anycast_address,
                       const char *        hostname,
                       guint32             timeout,
                       gboolean            use_fqdn,
@@ -180,7 +180,7 @@ merge_dhclient_config(NMDhcpDhclient *    self,
     new = nm_dhcp_dhclient_create_config(iface,
                                          addr_family,
                                          client_id,
-                                         anycast_addr,
+                                         anycast_address,
                                          hostname,
                                          timeout,
                                          use_fqdn,
@@ -280,7 +280,7 @@ create_dhclient_config(NMDhcpDhclient *    self,
                        const char *        iface,
                        const char *        uuid,
                        GBytes *            client_id,
-                       const char *        dhcp_anycast_addr,
+                       const char *        anycast_address,
                        const char *        hostname,
                        guint32             timeout,
                        gboolean            use_fqdn,
@@ -312,7 +312,7 @@ create_dhclient_config(NMDhcpDhclient *    self,
                                iface,
                                new,
                                client_id,
-                               dhcp_anycast_addr,
+                               anycast_address,
                                hostname,
                                timeout,
                                use_fqdn,
@@ -435,6 +435,12 @@ dhclient_start(NMDhcpClient *client,
     if (release)
         g_ptr_array_add(argv, (gpointer) "-r");
 
+    if (!release
+        && NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)),
+                        NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST)) {
+        g_ptr_array_add(argv, (gpointer) "-B");
+    }
+
     if (addr_family == AF_INET6) {
         g_ptr_array_add(argv, (gpointer) "-6");
 
@@ -508,10 +514,7 @@ dhclient_start(NMDhcpClient *client,
 }
 
 static gboolean
-ip4_start(NMDhcpClient *client,
-          const char *  dhcp_anycast_addr,
-          const char *  last_ip4_address,
-          GError **     error)
+ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error)
 {
     NMDhcpDhclient *       self = NM_DHCP_DHCLIENT(client);
     NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
@@ -520,19 +523,20 @@ ip4_start(NMDhcpClient *client,
 
     client_id = nm_dhcp_client_get_client_id(client);
 
-    priv->conf_file = create_dhclient_config(self,
-                                             AF_INET,
-                                             nm_dhcp_client_get_iface(client),
-                                             nm_dhcp_client_get_uuid(client),
-                                             client_id,
-                                             dhcp_anycast_addr,
-                                             nm_dhcp_client_get_hostname(client),
-                                             nm_dhcp_client_get_timeout(client),
-                                             nm_dhcp_client_get_use_fqdn(client),
-                                             nm_dhcp_client_get_hostname_flags(client),
-                                             nm_dhcp_client_get_mud_url(client),
-                                             nm_dhcp_client_get_reject_servers(client),
-                                             &new_client_id);
+    priv->conf_file = create_dhclient_config(
+        self,
+        AF_INET,
+        nm_dhcp_client_get_iface(client),
+        nm_dhcp_client_get_uuid(client),
+        client_id,
+        nm_dhcp_client_get_anycast_address(client),
+        nm_dhcp_client_get_hostname(client),
+        nm_dhcp_client_get_timeout(client),
+        NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_USE_FQDN),
+        nm_dhcp_client_get_hostname_flags(client),
+        nm_dhcp_client_get_mud_url(client),
+        nm_dhcp_client_get_reject_servers(client),
+        &new_client_id);
     if (!priv->conf_file) {
         nm_utils_error_set_literal(error,
                                    NM_UTILS_ERROR_UNKNOWN,
@@ -549,7 +553,6 @@ ip4_start(NMDhcpClient *client,
 
 static gboolean
 ip6_start(NMDhcpClient *            client,
-          const char *              dhcp_anycast_addr,
           const struct in6_addr *   ll_addr,
           NMSettingIP6ConfigPrivacy privacy,
           guint                     needed_prefixes,
@@ -566,7 +569,7 @@ ip6_start(NMDhcpClient *            client,
                                              nm_dhcp_client_get_iface(client),
                                              nm_dhcp_client_get_uuid(client),
                                              NULL,
-                                             dhcp_anycast_addr,
+                                             nm_dhcp_client_get_anycast_address(client),
                                              nm_dhcp_client_get_hostname(client),
                                              nm_dhcp_client_get_timeout(client),
                                              TRUE,
@@ -582,7 +585,10 @@ ip6_start(NMDhcpClient *            client,
     }
 
     return dhclient_start(client,
-                          nm_dhcp_client_get_info_only(NM_DHCP_CLIENT(self)) ? "-S" : "-N",
+                          NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)),
+                                       NM_DHCP_CLIENT_FLAGS_INFO_ONLY)
+                              ? "-S"
+                              : "-N",
                           FALSE,
                           NULL,
                           needed_prefixes,
diff --git a/src/core/dhcp/nm-dhcp-dhcpcanon.c b/src/core/dhcp/nm-dhcp-dhcpcanon.c
index 3504a048..f3a52ea9 100644
--- a/src/core/dhcp/nm-dhcp-dhcpcanon.c
+++ b/src/core/dhcp/nm-dhcp-dhcpcanon.c
@@ -161,10 +161,7 @@ dhcpcanon_start(NMDhcpClient *client,
 }
 
 static gboolean
-ip4_start(NMDhcpClient *client,
-          const char *  dhcp_anycast_addr,
-          const char *  last_ip4_address,
-          GError **     error)
+ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error)
 {
     return dhcpcanon_start(client, NULL, NULL, FALSE, NULL, 0, error);
 }
diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c
index cf9fe5c2..605fb84d 100644
--- a/src/core/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/core/dhcp/nm-dhcp-dhcpcd.c
@@ -65,10 +65,7 @@ nm_dhcp_dhcpcd_get_path(void)
 }
 
 static gboolean
-ip4_start(NMDhcpClient *client,
-          const char *  dhcp_anycast_addr,
-          const char *  last_ip4_address,
-          GError **     error)
+ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error)
 {
     NMDhcpDhcpcd *    self            = NM_DHCP_DHCPCD(client);
     gs_unref_ptrarray GPtrArray *argv = NULL;
@@ -122,7 +119,7 @@ ip4_start(NMDhcpClient *client,
     hostname = nm_dhcp_client_get_hostname(client);
 
     if (hostname) {
-        if (nm_dhcp_client_get_use_fqdn(client)) {
+        if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_USE_FQDN)) {
             g_ptr_array_add(argv, (gpointer) "-h");
             g_ptr_array_add(argv, (gpointer) hostname);
             g_ptr_array_add(argv, (gpointer) "-F");
@@ -169,27 +166,29 @@ stop(NMDhcpClient *client, gboolean release)
     int           sig, errsv;
 
     pid = nm_dhcp_client_get_pid(client);
-    sig = release ? SIGALRM : SIGTERM;
-    _LOGD("sending %s to dhcpcd pid %d", sig == SIGALRM ? "SIGALRM" : "SIGTERM", pid);
-
-    /* dhcpcd-9.x features privilege separation.
-     * It's not our job to track all these processes so we rely on dhcpcd
-     * to always cleanup after itself.
-     * Because it also re-parents itself to PID 1, the process cannot be
-     * reaped or waited for.
-     * As such, just send the correct signal.
-     */
-    if (kill(pid, sig) == -1) {
-        errsv = errno;
-        _LOGE("failed to kill dhcpcd %d:%s", errsv, strerror(errsv));
-    }
+    if (pid > 1) {
+        sig = release ? SIGALRM : SIGTERM;
+        _LOGD("sending %s to dhcpcd pid %d", sig == SIGALRM ? "SIGALRM" : "SIGTERM", pid);
+
+        /* dhcpcd-9.x features privilege separation.
+         * It's not our job to track all these processes so we rely on dhcpcd
+         * to always cleanup after itself.
+         * Because it also re-parents itself to PID 1, the process cannot be
+         * reaped or waited for.
+         * As such, just send the correct signal.
+         */
+        if (kill(pid, sig) == -1) {
+            errsv = errno;
+            _LOGE("failed to kill dhcpcd %d:%s", errsv, strerror(errsv));
+        }
 
-    /* When this function exits NM expects the PID to be -1.
-     * This means we also need to stop watching the pid.
-     * If we need to know the exit status then we need to refactor NM
-     * to allow a non -1 to mean we're waiting to exit still.
-     */
-    nm_dhcp_client_stop_watch_child(client, pid);
+        /* When this function exits NM expects the PID to be -1.
+         * This means we also need to stop watching the pid.
+         * If we need to know the exit status then we need to refactor NM
+         * to allow a non -1 to mean we're waiting to exit still.
+         */
+        nm_dhcp_client_stop_watch_child(client, pid);
+    }
 }
 
 /*****************************************************************************/
diff --git a/src/core/dhcp/nm-dhcp-helper.c b/src/core/dhcp/nm-dhcp-helper.c
index 0f98add1..56d20101 100644
--- a/src/core/dhcp/nm-dhcp-helper.c
+++ b/src/core/dhcp/nm-dhcp-helper.c
@@ -3,13 +3,13 @@
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
-#include "nm-glib-aux/nm-default-glib.h"
+#include "libnm-glib-aux/nm-default-glib.h"
 
 #include <unistd.h>
 #include <stdlib.h>
 #include <signal.h>
 
-#include "nm-utils/nm-vpn-plugin-macros.h"
+#include "libnm-glib-aux/nm-logging-syslog.h"
 
 #include "nm-dhcp-helper-api.h"
 
@@ -92,7 +92,7 @@ build_signal_parameters(void)
         g_variant_builder_add(&builder,
                               "{sv}",
                               name,
-                              g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, val, strlen(val), 1));
+                              nm_g_variant_new_ay((const guint8 *) val, strlen(val)));
 
 next:;
     }
@@ -154,7 +154,7 @@ do_connect:
                       error->message,
                       try_count,
                       (long long) (time_end - time_remaining - time_start) / 1000);
-                interval = NM_CLAMP((gint64)(100L * (1L << NM_MIN(try_count, 31))), 5000, 100000);
+                interval = NM_CLAMP((gint64) (100L * (1L << NM_MIN(try_count, 31))), 5000, 100000);
                 g_usleep(NM_MIN(interval, time_remaining));
                 g_clear_error(&error);
                 goto do_connect;
@@ -199,7 +199,7 @@ do_notify:
              * do some retry. */
             if (remaining_time > 0) {
                 _LOGi("failure to call notify: %s (retry %u)", error->message, try_count);
-                interval = NM_CLAMP((gint64)(100L * (1L << NM_MIN(try_count, 31))), 5000, 25000);
+                interval = NM_CLAMP((gint64) (100L * (1L << NM_MIN(try_count, 31))), 5000, 25000);
                 g_usleep(NM_MIN(interval, remaining_time));
                 g_clear_error(&error);
                 goto do_notify;
diff --git a/src/core/dhcp/nm-dhcp-listener.c b/src/core/dhcp/nm-dhcp-listener.c
index 1673bbb7..b8bb3c33 100644
--- a/src/core/dhcp/nm-dhcp-listener.c
+++ b/src/core/dhcp/nm-dhcp-listener.c
@@ -16,7 +16,7 @@
 #include "nm-dhcp-helper-api.h"
 #include "nm-dhcp-client.h"
 #include "nm-dhcp-manager.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
 #include "NetworkManagerUtils.h"
 
diff --git a/src/core/dhcp/nm-dhcp-manager.c b/src/core/dhcp/nm-dhcp-manager.c
index bc114ad8..44b8ede2 100644
--- a/src/core/dhcp/nm-dhcp-manager.c
+++ b/src/core/dhcp/nm-dhcp-manager.c
@@ -16,8 +16,8 @@
 #include <fcntl.h>
 #include <stdio.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "systemd/nm-sd-utils-shared.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
@@ -213,12 +213,11 @@ client_start(NMDhcpManager *           self,
              guint32                   iaid,
              gboolean                  iaid_explicit,
              guint32                   timeout,
-             const char *              dhcp_anycast_addr,
+             NMDhcpClientFlags         client_flags,
+             const char *              anycast_address,
              const char *              hostname,
-             gboolean                  hostname_use_fqdn,
              NMDhcpHostnameFlags       hostname_flags,
              const char *              mud_url,
-             gboolean                  info_only,
              NMSettingIP6ConfigPrivacy privacy,
              const char *              last_ip4_address,
              guint                     needed_prefixes,
@@ -241,6 +240,7 @@ client_start(NMDhcpManager *           self,
                              || g_bytes_get_size(vendor_class_identifier) <= 255,
                          NULL);
     g_return_val_if_fail(!error || !*error, NULL);
+    nm_assert(!NM_FLAGS_ANY(client_flags, ~NM_DHCP_CLIENT_FLAGS_ALL));
 
     if (addr_family == AF_INET) {
         if (!hwaddr || !bcast_hwaddr) {
@@ -252,7 +252,7 @@ client_start(NMDhcpManager *           self,
         }
 
         hwaddr_len = g_bytes_get_size(hwaddr);
-        if (hwaddr_len == 0 || hwaddr_len > NM_UTILS_HWADDR_LEN_MAX) {
+        if (hwaddr_len == 0 || hwaddr_len > _NM_UTILS_HWADDR_LEN_MAX) {
             nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address");
             g_return_val_if_reached(NULL);
         }
@@ -263,12 +263,14 @@ client_start(NMDhcpManager *           self,
     }
 
     if (hostname) {
-        if ((hostname_use_fqdn && !nm_sd_dns_name_is_valid(hostname))
-            || (!hostname_use_fqdn && !nm_sd_hostname_is_valid(hostname, FALSE))) {
+        gboolean use_fqdn = NM_FLAGS_HAS(client_flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN);
+
+        if ((use_fqdn && !nm_sd_dns_name_is_valid(hostname))
+            || (!use_fqdn && !nm_sd_hostname_is_valid(hostname, FALSE))) {
             nm_log_warn(LOGD_DHCP,
                         "dhcp%c: %s '%s' is invalid, will be ignored",
                         nm_utils_addr_family_to_char(addr_family),
-                        hostname_use_fqdn ? "FQDN" : "hostname",
+                        use_fqdn ? "FQDN" : "hostname",
                         hostname);
             hostname = NULL;
         }
@@ -334,8 +336,9 @@ client_start(NMDhcpManager *           self,
                           NM_DHCP_CLIENT_REJECT_SERVERS,
                           reject_servers,
                           NM_DHCP_CLIENT_FLAGS,
-                          (guint)(0 | (hostname_use_fqdn ? NM_DHCP_CLIENT_FLAGS_USE_FQDN : 0)
-                                  | (info_only ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY : 0)),
+                          (guint) client_flags,
+                          NM_DHCP_CLIENT_ANYCAST_ADDRESS,
+                          anycast_address,
                           NULL);
     nm_assert(client && c_list_is_empty(&client->dhcp_client_lst));
     c_list_link_tail(&priv->dhcp_client_lst_head, &client->dhcp_client_lst);
@@ -371,16 +374,11 @@ client_start(NMDhcpManager *           self,
      */
 
     if (addr_family == AF_INET) {
-        success = nm_dhcp_client_start_ip4(client,
-                                           dhcp_client_id,
-                                           dhcp_anycast_addr,
-                                           last_ip4_address,
-                                           error);
+        success = nm_dhcp_client_start_ip4(client, dhcp_client_id, last_ip4_address, error);
     } else {
         success = nm_dhcp_client_start_ip6(client,
                                            dhcp_client_id,
                                            enforce_duid,
-                                           dhcp_anycast_addr,
                                            ipv6_ll_addr,
                                            privacy,
                                            needed_prefixes,
@@ -406,6 +404,7 @@ nm_dhcp_manager_start_ip4(NMDhcpManager *     self,
                           const char *        uuid,
                           guint32             route_table,
                           guint32             route_metric,
+                          NMDhcpClientFlags   client_flags,
                           gboolean            send_hostname,
                           const char *        dhcp_hostname,
                           const char *        dhcp_fqdn,
@@ -413,7 +412,7 @@ nm_dhcp_manager_start_ip4(NMDhcpManager *     self,
                           const char *        mud_url,
                           GBytes *            dhcp_client_id,
                           guint32             timeout,
-                          const char *        dhcp_anycast_addr,
+                          const char *        anycast_address,
                           const char *        last_ip_address,
                           GBytes *            vendor_class_identifier,
                           const char *const * reject_servers,
@@ -425,6 +424,10 @@ nm_dhcp_manager_start_ip4(NMDhcpManager *     self,
     gboolean              use_fqdn     = FALSE;
     char *                dot;
 
+    /* these flags are set automatically/prohibited, and not free to set to the caller.  */
+    nm_assert(!NM_FLAGS_ANY(client_flags,
+                            NM_DHCP_CLIENT_FLAGS_USE_FQDN | NM_DHCP_CLIENT_FLAGS_INFO_ONLY));
+
     g_return_val_if_fail(NM_IS_DHCP_MANAGER(self), NULL);
     priv = NM_DHCP_MANAGER_GET_PRIVATE(self);
 
@@ -451,34 +454,34 @@ nm_dhcp_manager_start_ip4(NMDhcpManager *     self,
         }
     }
 
-    return client_start(self,
-                        AF_INET,
-                        multi_idx,
-                        iface,
-                        ifindex,
-                        hwaddr,
-                        bcast_hwaddr,
-                        uuid,
-                        route_table,
-                        route_metric,
-                        NULL,
-                        dhcp_client_id,
-                        FALSE,
-                        0,
-                        FALSE,
-                        timeout,
-                        dhcp_anycast_addr,
-                        hostname,
-                        use_fqdn,
-                        hostname_flags,
-                        mud_url,
-                        FALSE,
-                        0,
-                        last_ip_address,
-                        0,
-                        vendor_class_identifier,
-                        reject_servers,
-                        error);
+    return client_start(
+        self,
+        AF_INET,
+        multi_idx,
+        iface,
+        ifindex,
+        hwaddr,
+        bcast_hwaddr,
+        uuid,
+        route_table,
+        route_metric,
+        NULL,
+        dhcp_client_id,
+        FALSE,
+        0,
+        FALSE,
+        timeout,
+        client_flags | (use_fqdn ? NM_DHCP_CLIENT_FLAGS_USE_FQDN : NM_DHCP_CLIENT_FLAGS_NONE),
+        anycast_address,
+        hostname,
+        hostname_flags,
+        mud_url,
+        0,
+        last_ip_address,
+        0,
+        vendor_class_identifier,
+        reject_servers,
+        error);
 }
 
 /* Caller owns a reference to the NMDhcpClient on return */
@@ -491,6 +494,7 @@ nm_dhcp_manager_start_ip6(NMDhcpManager *           self,
                           const char *              uuid,
                           guint32                   route_table,
                           guint32                   route_metric,
+                          NMDhcpClientFlags         client_flags,
                           gboolean                  send_hostname,
                           const char *              dhcp_hostname,
                           NMDhcpHostnameFlags       hostname_flags,
@@ -500,8 +504,7 @@ nm_dhcp_manager_start_ip6(NMDhcpManager *           self,
                           guint32                   iaid,
                           gboolean                  iaid_explicit,
                           guint32                   timeout,
-                          const char *              dhcp_anycast_addr,
-                          gboolean                  info_only,
+                          const char *              anycast_address,
                           NMSettingIP6ConfigPrivacy privacy,
                           guint                     needed_prefixes,
                           GError **                 error)
@@ -509,6 +512,9 @@ nm_dhcp_manager_start_ip6(NMDhcpManager *           self,
     NMDhcpManagerPrivate *priv;
     const char *          hostname = NULL;
 
+    /* this flag is set automatically, and not free to set to the caller.  */
+    nm_assert(!NM_FLAGS_ANY(client_flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN));
+
     g_return_val_if_fail(NM_IS_DHCP_MANAGER(self), NULL);
     priv = NM_DHCP_MANAGER_GET_PRIVATE(self);
 
@@ -532,12 +538,11 @@ nm_dhcp_manager_start_ip6(NMDhcpManager *           self,
                         iaid,
                         iaid_explicit,
                         timeout,
-                        dhcp_anycast_addr,
+                        client_flags | NM_DHCP_CLIENT_FLAGS_USE_FQDN,
+                        anycast_address,
                         hostname,
-                        TRUE,
                         hostname_flags,
                         mud_url,
-                        info_only,
                         privacy,
                         NULL,
                         needed_prefixes,
diff --git a/src/core/dhcp/nm-dhcp-manager.h b/src/core/dhcp/nm-dhcp-manager.h
index f7aba8a0..ce160437 100644
--- a/src/core/dhcp/nm-dhcp-manager.h
+++ b/src/core/dhcp/nm-dhcp-manager.h
@@ -41,6 +41,7 @@ NMDhcpClient *nm_dhcp_manager_start_ip4(NMDhcpManager *            manager,
                                         const char *               uuid,
                                         guint32                    route_table,
                                         guint32                    route_metric,
+                                        NMDhcpClientFlags          client_flags,
                                         gboolean                   send_hostname,
                                         const char *               dhcp_hostname,
                                         const char *               dhcp_fqdn,
@@ -48,7 +49,7 @@ NMDhcpClient *nm_dhcp_manager_start_ip4(NMDhcpManager *            manager,
                                         const char *               mud_url,
                                         GBytes *                   dhcp_client_id,
                                         guint32                    timeout,
-                                        const char *               dhcp_anycast_addr,
+                                        const char *               anycast_address,
                                         const char *               last_ip_address,
                                         GBytes *                   vendor_class_identifier,
                                         const char *const *        reject_servers,
@@ -62,6 +63,7 @@ NMDhcpClient *nm_dhcp_manager_start_ip6(NMDhcpManager *            manager,
                                         const char *               uuid,
                                         guint32                    route_table,
                                         guint32                    route_metric,
+                                        NMDhcpClientFlags          client_flags,
                                         gboolean                   send_hostname,
                                         const char *               dhcp_hostname,
                                         NMDhcpHostnameFlags        hostname_flags,
@@ -71,8 +73,7 @@ NMDhcpClient *nm_dhcp_manager_start_ip6(NMDhcpManager *            manager,
                                         guint32                    iaid,
                                         gboolean                   iaid_explicit,
                                         guint32                    timeout,
-                                        const char *               dhcp_anycast_addr,
-                                        gboolean                   info_only,
+                                        const char *               anycast_address,
                                         NMSettingIP6ConfigPrivacy  privacy,
                                         guint                      needed_prefixes,
                                         GError **                  error);
diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c
index 116e1bdb..fe71f587 100644
--- a/src/core/dhcp/nm-dhcp-nettools.c
+++ b/src/core/dhcp/nm-dhcp-nettools.c
@@ -13,9 +13,9 @@
 #include <ctype.h>
 #include <net/if_arp.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-std-aux/unaligned.h"
-#include "nm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-std-aux/unaligned.h"
+#include "libnm-glib-aux/nm-str-buf.h"
 
 #include "nm-utils.h"
 #include "nm-config.h"
@@ -23,11 +23,11 @@
 #include "nm-dhcp-options.h"
 #include "nm-core-utils.h"
 #include "NetworkManagerUtils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-dhcp-client-logging.h"
 #include "n-dhcp4/src/n-dhcp4.h"
-#include "systemd/nm-sd-utils-shared.h"
-#include "systemd/nm-sd-utils-dhcp.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
+#include "libnm-systemd-core/nm-sd-utils-dhcp.h"
 
 /*****************************************************************************/
 
@@ -342,16 +342,16 @@ lease_parse_routes(NDhcp4ClientLease *lease,
     const guint8 *l_data;
     gsize         l_data_len;
     int           r;
+    guint         i;
 
-    r = _client_lease_query(lease,
-                            NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE,
-                            &l_data,
-                            &l_data_len);
-    if (r == 0) {
-        nm_str_buf_reset(sbuf);
+    for (i = 0; i < 2; i++) {
+        const guint8 option_code = (i == 0) ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
+                                            : NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE;
 
-        has_classless = TRUE;
+        if (_client_lease_query(lease, option_code, &l_data, &l_data_len) != 0)
+            continue;
 
+        nm_str_buf_reset(sbuf);
         while (lease_option_consume_route(&l_data, &l_data_len, TRUE, &dest, &plen, &gateway)) {
             _nm_utils_inet4_ntop(dest, dest_str);
             _nm_utils_inet4_ntop(gateway, gateway_str);
@@ -359,6 +359,11 @@ lease_parse_routes(NDhcp4ClientLease *lease,
             nm_str_buf_append_required_delimiter(sbuf, ' ');
             nm_str_buf_append_printf(sbuf, "%s/%d %s", dest_str, (int) plen, gateway_str);
 
+            if (has_classless) {
+                /* Ignore private option if the standard one is present */
+                continue;
+            }
+
             if (plen == 0) {
                 /* if there are multiple default routes, we add them with differing
                  * metrics. */
@@ -384,10 +389,8 @@ lease_parse_routes(NDhcp4ClientLease *lease,
                 NULL);
         }
 
-        nm_dhcp_option_add_option(options,
-                                  AF_INET,
-                                  NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE,
-                                  nm_str_buf_get_str(sbuf));
+        has_classless = TRUE;
+        nm_dhcp_option_add_option(options, AF_INET, option_code, nm_str_buf_get_str(sbuf));
     }
 
     r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, &l_data, &l_data_len);
@@ -693,8 +696,8 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
 
         v_str = nm_utils_buf_utf8safe_escape((char *) l_data, l_data_len, 0, &to_free);
 
-        nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, v_str);
-        nm_ip4_config_set_nis_domain(ip4_config, v_str);
+        nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, v_str ?: "");
+        nm_ip4_config_set_nis_domain(ip4_config, v_str ?: "");
     }
 
     lease_parse_address_list(lease, ip4_config, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf);
@@ -871,7 +874,7 @@ dhcp4_event_cb(int fd, GIOCondition condition, gpointer user_data)
 }
 
 static gboolean
-nettools_create(NMDhcpNettools *self, const char *dhcp_anycast_addr, GError **error)
+nettools_create(NMDhcpNettools *self, GError **error)
 {
     NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self);
     nm_auto(n_dhcp4_client_config_freep) NDhcp4ClientConfig *config = NULL;
@@ -890,6 +893,8 @@ nettools_create(NMDhcpNettools *self, const char *dhcp_anycast_addr, GError **er
 
     g_return_val_if_fail(!priv->client, FALSE);
 
+    /* TODO: honor nm_dhcp_client_get_anycast_address() */
+
     hwaddr = nm_dhcp_client_get_hw_addr(NM_DHCP_CLIENT(self));
     if (!hwaddr || !(hwaddr_arr = g_bytes_get_data(hwaddr, &hwaddr_len))
         || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen(hwaddr_len)) < 0) {
@@ -938,9 +943,13 @@ nettools_create(NMDhcpNettools *self, const char *dhcp_anycast_addr, GError **er
     n_dhcp4_client_config_set_transport(config, transport);
     n_dhcp4_client_config_set_mac(config, hwaddr_arr, hwaddr_len);
     n_dhcp4_client_config_set_broadcast_mac(config, bcast_hwaddr_arr, bcast_hwaddr_len);
+    n_dhcp4_client_config_set_request_broadcast(
+        config,
+        NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)),
+                     NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST));
     r = n_dhcp4_client_config_set_client_id(config,
                                             client_id_arr,
-                                            NM_MIN(client_id_len, 1 + _NM_SD_MAX_CLIENT_ID_LEN));
+                                            NM_MIN(client_id_len, 1 + _NM_MAX_CLIENT_ID_LEN));
     if (r) {
         set_error_nettools(error, r, "failed to set client-id");
         return FALSE;
@@ -1028,10 +1037,7 @@ fqdn_flags_to_wire(NMDhcpHostnameFlags flags)
 }
 
 static gboolean
-ip4_start(NMDhcpClient *client,
-          const char *  dhcp_anycast_addr,
-          const char *  last_ip4_address,
-          GError **     error)
+ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error)
 {
     nm_auto(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL;
     NMDhcpNettools *       self       = NM_DHCP_NETTOOLS(client);
@@ -1045,7 +1051,7 @@ ip4_start(NMDhcpClient *client,
 
     g_return_val_if_fail(!priv->probe, FALSE);
 
-    if (!nettools_create(self, dhcp_anycast_addr, error))
+    if (!nettools_create(self, error))
         return FALSE;
 
     r = n_dhcp4_client_probe_config_new(&config);
@@ -1110,7 +1116,7 @@ ip4_start(NMDhcpClient *client,
     }
     hostname = nm_dhcp_client_get_hostname(client);
     if (hostname) {
-        if (nm_dhcp_client_get_use_fqdn(client)) {
+        if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_USE_FQDN)) {
             uint8_t             buffer[255];
             NMDhcpHostnameFlags flags;
             size_t              fqdn_len;
diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c
index 3537cd14..8f0d7408 100644
--- a/src/core/dhcp/nm-dhcp-options.c
+++ b/src/core/dhcp/nm-dhcp-options.c
@@ -7,7 +7,7 @@
 
 #include "nm-dhcp-options.h"
 
-#include "nm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-str-buf.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c
index b92a9073..c789aaee 100644
--- a/src/core/dhcp/nm-dhcp-systemd.c
+++ b/src/core/dhcp/nm-dhcp-systemd.c
@@ -13,18 +13,18 @@
 #include <ctype.h>
 #include <net/if_arp.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-std-aux/unaligned.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-std-aux/unaligned.h"
 
 #include "nm-utils.h"
 #include "nm-dhcp-utils.h"
 #include "nm-dhcp-options.h"
 #include "nm-core-utils.h"
 #include "NetworkManagerUtils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-dhcp-client-logging.h"
-#include "systemd/nm-sd.h"
-#include "systemd/nm-sd-utils-dhcp.h"
+#include "libnm-systemd-core/nm-sd.h"
+#include "libnm-systemd-core/nm-sd-utils-dhcp.h"
 
 /*****************************************************************************/
 
@@ -85,29 +85,29 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
     const struct in_addr *         addr_list;
     char                           addr_str[NM_UTILS_INET_ADDRSTRLEN];
     const char *                   s;
-    nm_auto_free_gstring GString *str      = NULL;
-    gs_free sd_dhcp_route **routes         = NULL;
-    const char *const *     search_domains = NULL;
-    guint16                 mtu;
-    int                     i, num;
-    const void *            data;
-    gsize                   data_len;
-    gboolean                metered                   = FALSE;
-    gboolean                has_router_from_classless = FALSE;
-    gboolean                has_classless_route       = FALSE;
-    gboolean                has_static_route          = FALSE;
-    const gint32            ts                        = nm_utils_get_monotonic_timestamp_sec();
-    gint64                  ts_time                   = time(NULL);
-    struct in_addr          a_address;
-    struct in_addr          a_netmask;
-    struct in_addr          a_next_server;
-    struct in_addr          server_id;
-    struct in_addr          broadcast;
-    const struct in_addr *  a_router;
-    guint32                 a_plen;
-    guint32                 a_lifetime;
-    guint32                 renewal;
-    guint32                 rebinding;
+    nm_auto_free_gstring GString *str           = NULL;
+    nm_auto_free sd_dhcp_route **routes         = NULL;
+    const char *const *          search_domains = NULL;
+    guint16                      mtu;
+    int                          i, num;
+    const void *                 data;
+    gsize                        data_len;
+    gboolean                     metered                   = FALSE;
+    gboolean                     has_router_from_classless = FALSE;
+    gboolean                     has_classless_route       = FALSE;
+    gboolean                     has_static_route          = FALSE;
+    const gint32                 ts                        = nm_utils_get_monotonic_timestamp_sec();
+    gint64                       ts_time                   = time(NULL);
+    struct in_addr               a_address;
+    struct in_addr               a_netmask;
+    struct in_addr               a_next_server;
+    struct in_addr               server_id;
+    struct in_addr               broadcast;
+    const struct in_addr *       a_router;
+    guint32                      a_plen;
+    guint32                      a_lifetime;
+    guint32                      renewal;
+    guint32                      rebinding;
     gs_free nm_sd_dhcp_option *private_options = NULL;
 
     nm_assert(lease != NULL);
@@ -153,7 +153,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
     nm_dhcp_option_add_option_u64(options,
                                   AF_INET,
                                   NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
-                                  (guint64)(ts_time + a_lifetime));
+                                  (guint64) (ts_time + a_lifetime));
 
     if (sd_dhcp_lease_get_next_server(lease, &a_next_server) == 0) {
         _nm_utils_inet4_ntop(a_next_server.s_addr, addr_str);
@@ -429,18 +429,33 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
     num = nm_sd_dhcp_lease_get_private_options(lease, &private_options);
     if (num > 0) {
         for (i = 0; i < num; i++) {
-            char *option_string;
-
-            option_string = nm_utils_bin2hexstr_full(private_options[i].data,
-                                                     private_options[i].data_len,
-                                                     ':',
-                                                     FALSE,
-                                                     NULL);
-            if (!options) {
-                g_free(option_string);
+            guint8        code       = private_options[i].code;
+            const guint8 *l_data     = private_options[i].data;
+            gsize         l_data_len = private_options[i].data_len;
+            char *        option_string;
+
+            if (code == NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY) {
+                if (nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) {
+                    nm_dhcp_option_add_option_utf8safe_escape(
+                        options,
+                        AF_INET,
+                        NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY,
+                        l_data,
+                        l_data_len);
+                }
                 continue;
             }
-            nm_dhcp_option_take_option(options, AF_INET, private_options[i].code, option_string);
+            if (code == NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE) {
+                /* nettools and dhclient parse option 249 (Microsoft Classless Static Route)
+                 * as fallback for routes and ignores them from private options.
+                 *
+                 * The systemd plugin does not, and for consistency with nettools we
+                 * also don't expose it as private option either. */
+                continue;
+            }
+
+            option_string = nm_utils_bin2hexstr_full(l_data, l_data_len, ':', FALSE, NULL);
+            nm_dhcp_option_take_option(options, AF_INET, code, option_string);
         }
     }
     NM_SET_OUT(out_options, g_steal_pointer(&options));
@@ -543,10 +558,7 @@ dhcp_event_cb(sd_dhcp_client *client, int event, gpointer user_data)
 }
 
 static gboolean
-ip4_start(NMDhcpClient *client,
-          const char *  dhcp_anycast_addr,
-          const char *  last_ip4_address,
-          GError **     error)
+ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error)
 {
     nm_auto(sd_dhcp_client_unrefp) sd_dhcp_client *sd_client  = NULL;
     NMDhcpSystemd *                                self       = NM_DHCP_SYSTEMD(client);
@@ -572,6 +584,8 @@ ip4_start(NMDhcpClient *client,
     g_return_val_if_fail(!priv->client4, FALSE);
     g_return_val_if_fail(!priv->client6, FALSE);
 
+    /* TODO: honor nm_dhcp_client_get_anycast_address() */
+
     r = sd_dhcp_client_new(&sd_client, FALSE);
     if (r < 0) {
         nm_utils_error_set_errno(error, r, "failed to create dhcp-client: %s");
@@ -632,6 +646,11 @@ ip4_start(NMDhcpClient *client,
             sd_dhcp_lease_get_address(lease, &last_addr);
     }
 
+    r = sd_dhcp_client_set_request_broadcast(sd_client,
+                                             NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client),
+                                                          NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST));
+    nm_assert(r >= 0);
+
     if (last_addr.s_addr) {
         r = sd_dhcp_client_set_request_address(sd_client, &last_addr);
         if (r < 0) {
@@ -659,7 +678,7 @@ ip4_start(NMDhcpClient *client,
     r = sd_dhcp_client_set_client_id(sd_client,
                                      client_id_arr[0],
                                      client_id_arr + 1,
-                                     NM_MIN(client_id_len - 1, _NM_SD_MAX_CLIENT_ID_LEN));
+                                     NM_MIN(client_id_len - 1, _NM_MAX_CLIENT_ID_LEN));
     if (r < 0) {
         nm_utils_error_set_errno(error, r, "failed to set IPv4 client-id: %s");
         return FALSE;
@@ -844,14 +863,16 @@ bound6_handle(NMDhcpSystemd *self)
 
     _LOGD("lease available");
 
-    ip6_config = lease_to_ip6_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)),
-                                     iface,
-                                     nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)),
-                                     lease,
-                                     nm_dhcp_client_get_info_only(NM_DHCP_CLIENT(self)),
-                                     &options,
-                                     ts,
-                                     &error);
+    ip6_config =
+        lease_to_ip6_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)),
+                            iface,
+                            nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)),
+                            lease,
+                            NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)),
+                                         NM_DHCP_CLIENT_FLAGS_INFO_ONLY),
+                            &options,
+                            ts,
+                            &error);
 
     if (!ip6_config) {
         _LOGW("%s", error->message);
@@ -905,7 +926,6 @@ dhcp6_event_cb(sd_dhcp6_client *client, int event, gpointer user_data)
 
 static gboolean
 ip6_start(NMDhcpClient *            client,
-          const char *              dhcp_anycast_addr,
           const struct in6_addr *   ll_addr,
           NMSettingIP6ConfigPrivacy privacy,
           guint                     needed_prefixes,
@@ -924,6 +944,8 @@ ip6_start(NMDhcpClient *            client,
     g_return_val_if_fail(!priv->client4, FALSE);
     g_return_val_if_fail(!priv->client6, FALSE);
 
+    /* TODO: honor nm_dhcp_client_get_anycast_address() */
+
     if (!(duid = nm_dhcp_client_get_client_id(client))
         || !(duid_arr = g_bytes_get_data(duid, &duid_len)) || duid_len < 2) {
         nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "missing DUID");
@@ -938,7 +960,7 @@ ip6_start(NMDhcpClient *            client,
 
     _LOGT("dhcp-client6: set %p", sd_client);
 
-    if (nm_dhcp_client_get_info_only(client)) {
+    if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_INFO_ONLY)) {
         sd_dhcp6_client_set_address_request(sd_client, 0);
         if (needed_prefixes == 0)
             sd_dhcp6_client_set_information_request(sd_client, 1);
diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c
index 646411e2..7fd18b0a 100644
--- a/src/core/dhcp/nm-dhcp-utils.c
+++ b/src/core/dhcp/nm-dhcp-utils.c
@@ -8,18 +8,18 @@
 #include <unistd.h>
 #include <arpa/inet.h>
 
-#include "nm-std-aux/unaligned.h"
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-glib-aux/nm-str-buf.h"
-#include "systemd/nm-sd-utils-shared.h"
+#include "libnm-std-aux/unaligned.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
 #include "nm-dhcp-utils.h"
 #include "nm-utils.h"
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-dhcp-client-logging.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 /*****************************************************************************/
 
@@ -880,7 +880,7 @@ nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_l
         n_data--;
 
     if (n_data > 0) {
-        if (memchr(data, n_data, '\0')) {
+        if (memchr(data, '\0', n_data)) {
             /* we accept trailing NUL, but none in between.
              *
              * https://tools.ietf.org/html/rfc2132#section-2
@@ -989,7 +989,7 @@ lease_option_print_label(NMStrBuf *sbuf, size_t n_label, const uint8_t **datap,
             break;
         case '.':
         case '\\':
-            nm_str_buf_append_c2(sbuf, '\\', c);
+            nm_str_buf_append_c(sbuf, '\\', c);
             break;
         default:
             nm_str_buf_append_printf(sbuf, "\\%3d", c);
diff --git a/src/core/dhcp/tests/test-dhcp-dhclient.c b/src/core/dhcp/tests/test-dhcp-dhclient.c
index 77626f69..e9a62096 100644
--- a/src/core/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/core/dhcp/tests/test-dhcp-dhclient.c
@@ -9,14 +9,14 @@
 #include <arpa/inet.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
 
 #include "NetworkManagerUtils.h"
 #include "dhcp/nm-dhcp-dhclient-utils.h"
 #include "dhcp/nm-dhcp-utils.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #include "nm-test-utils-core.h"
 
diff --git a/src/core/dhcp/tests/test-dhcp-utils.c b/src/core/dhcp/tests/test-dhcp-utils.c
index 9b54e2cd..4d47e7e2 100644
--- a/src/core/dhcp/tests/test-dhcp-utils.c
+++ b/src/core/dhcp/tests/test-dhcp-utils.c
@@ -9,12 +9,12 @@
 #include <arpa/inet.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
 #include "nm-utils.h"
 
 #include "dhcp/nm-dhcp-utils.h"
 #include "dhcp/nm-dhcp-options.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #include "nm-test-utils-core.h"
 
diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c
index dcff98e9..126e0a21 100644
--- a/src/core/dns/nm-dns-dnsmasq.c
+++ b/src/core/dns/nm-dns-dnsmasq.c
@@ -15,9 +15,9 @@
 #include <sys/stat.h>
 #include <linux/if.h>
 
-#include "nm-glib-aux/nm-dbus-aux.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
diff --git a/src/core/dns/nm-dns-dnsmasq.h b/src/core/dns/nm-dns-dnsmasq.h
index bd6d4c60..575b0b26 100644
--- a/src/core/dns/nm-dns-dnsmasq.h
+++ b/src/core/dns/nm-dns-dnsmasq.h
@@ -7,6 +7,7 @@
 #define __NETWORKMANAGER_DNS_DNSMASQ_H__
 
 #include "nm-dns-plugin.h"
+#include "nm-dns-manager.h"
 
 #define NM_TYPE_DNS_DNSMASQ (nm_dns_dnsmasq_get_type())
 #define NM_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq))
diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c
index f318b9b8..4c498c82 100644
--- a/src/core/dns/nm-dns-manager.c
+++ b/src/core/dns/nm-dns-manager.c
@@ -22,8 +22,9 @@
     #include <libpsl.h>
 #endif
 
+#include "libnm-glib-aux/nm-str-buf.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dns-manager.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -83,13 +84,10 @@ typedef struct {
     GHashTable *configs_dict;
     CList       configs_lst_head;
 
-    CList     ip_configs_lst_head;
+    CList     ip_config_lst_head;
     GVariant *config_variant;
 
-    NMDnsConfigIPData *best_ip_config_4;
-    NMDnsConfigIPData *best_ip_config_6;
-
-    bool ip_configs_lst_need_sort : 1;
+    bool ip_config_lst_need_sort : 1;
 
     bool configs_lst_need_sort : 1;
 
@@ -266,9 +264,11 @@ _dns_config_ip_data_new(NMDnsConfigData * data,
         .ip_config_type = ip_config_type,
     };
     c_list_link_tail(&data->data_lst_head, &ip_data->data_lst);
-    c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_configs_lst_head,
+    c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_config_lst_head,
                      &ip_data->ip_config_lst);
 
+    /* We also need to set priv->ip_config_lst_need_sort, but the caller will do that! */
+
     g_signal_connect(ip_config,
                      NM_IS_IP4_CONFIG(ip_config) ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY
                                                  : "notify::" NM_IP6_CONFIG_DNS_PRIORITY,
@@ -325,7 +325,7 @@ _dns_config_data_free(NMDnsConfigData *data)
 }
 
 static int
-_mgr_get_ip_configs_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data)
+_mgr_get_ip_config_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data)
 {
     const NMDnsConfigIPData *a = c_list_entry(a_lst, NMDnsConfigIPData, ip_config_lst);
     const NMDnsConfigIPData *b = c_list_entry(b_lst, NMDnsConfigIPData, ip_config_lst);
@@ -341,16 +341,16 @@ _mgr_get_ip_configs_lst_cmp(const CList *a_lst, const CList *b_lst, const void *
 }
 
 static CList *
-_mgr_get_ip_configs_lst_head(NMDnsManager *self)
+_mgr_get_ip_config_lst_head(NMDnsManager *self)
 {
     NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
-    if (G_UNLIKELY(priv->ip_configs_lst_need_sort)) {
-        priv->ip_configs_lst_need_sort = FALSE;
-        c_list_sort(&priv->ip_configs_lst_head, _mgr_get_ip_configs_lst_cmp, NULL);
+    if (G_UNLIKELY(priv->ip_config_lst_need_sort)) {
+        priv->ip_config_lst_need_sort = FALSE;
+        c_list_sort(&priv->ip_config_lst_head, _mgr_get_ip_config_lst_cmp, NULL);
     }
 
-    return &priv->ip_configs_lst_head;
+    return &priv->ip_config_lst_head;
 }
 
 static int
@@ -378,11 +378,11 @@ _mgr_get_configs_lst_head(NMDnsManager *self)
 
 /*****************************************************************************/
 
-gboolean
-nm_dns_manager_has_systemd_resolved(NMDnsManager *self)
+NMDnsPlugin *
+nm_dns_manager_get_systemd_resolved(NMDnsManager *self)
 {
-    NMDnsManagerPrivate * priv;
-    NMDnsSystemdResolved *plugin = NULL;
+    NMDnsManagerPrivate *priv;
+    NMDnsPlugin *        plugin = NULL;
 
     g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
 
@@ -390,11 +390,14 @@ nm_dns_manager_has_systemd_resolved(NMDnsManager *self)
 
     if (priv->sd_resolve_plugin) {
         nm_assert(!NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin));
-        plugin = NM_DNS_SYSTEMD_RESOLVED(priv->sd_resolve_plugin);
+        plugin = priv->sd_resolve_plugin;
     } else if (NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin))
-        plugin = NM_DNS_SYSTEMD_RESOLVED(priv->plugin);
+        plugin = priv->plugin;
 
-    return plugin && nm_dns_systemd_resolved_is_running(plugin);
+    if (plugin && nm_dns_systemd_resolved_is_running(NM_DNS_SYSTEMD_RESOLVED(plugin)))
+        return plugin;
+
+    return NULL;
 }
 
 /*****************************************************************************/
@@ -1104,7 +1107,7 @@ update_resolv_conf(NMDnsManager *                self,
 }
 
 static void
-compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN])
+compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[static HASH_LEN])
 {
     nm_auto_free_checksum GChecksum *sum = NULL;
     NMDnsConfigIPData *              ip_data;
@@ -1119,7 +1122,7 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[
 
         /* FIXME(ip-config-checksum): this relies on the fact that an IP
          * configuration without DNS parameters gives a zero checksum. */
-        head = _mgr_get_ip_configs_lst_head(self);
+        head = _mgr_get_ip_config_lst_head(self);
         c_list_for_each_entry (ip_data, head, ip_config_lst)
             nm_ip_config_hash(ip_data->ip_config, sum, TRUE);
     }
@@ -1169,27 +1172,26 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
 }
 
 static const char *
-get_nameserver_list(const NMIPConfig *config, GString **str)
+get_nameserver_list(const NMIPConfig *config, NMStrBuf *tmp_strbuf)
 {
-    guint num, i;
     char  buf[NM_UTILS_INET_ADDRSTRLEN];
     int   addr_family;
+    guint num;
+    guint i;
 
-    if (*str)
-        g_string_truncate(*str, 0);
-    else
-        *str = g_string_sized_new(64);
+    nm_str_buf_reset(tmp_strbuf);
 
     addr_family = nm_ip_config_get_addr_family(config);
     num         = nm_ip_config_get_num_nameservers(config);
     for (i = 0; i < num; i++) {
         nm_utils_inet_ntop(addr_family, nm_ip_config_get_nameserver(config, i), buf);
         if (i > 0)
-            g_string_append_c(*str, ' ');
-        g_string_append(*str, buf);
+            nm_str_buf_append_c(tmp_strbuf, ' ');
+        nm_str_buf_append(tmp_strbuf, buf);
     }
 
-    return (*str)->str;
+    nm_str_buf_maybe_expand(tmp_strbuf, 1, FALSE);
+    return nm_str_buf_get_str(tmp_strbuf);
 }
 
 static char **
@@ -1224,13 +1226,14 @@ _collect_resolv_conf_data(NMDnsManager *     self,
     if (global_config)
         merge_global_dns_config(&rc, global_config);
     else {
-        nm_auto_free_gstring GString *tmp_gstring = NULL;
-        int                           prio, first_prio = 0;
-        const NMDnsConfigIPData *     ip_data;
-        const CList *                 head;
-        gboolean                      is_first = TRUE;
-
-        head = _mgr_get_ip_configs_lst_head(self);
+        nm_auto_str_buf NMStrBuf tmp_strbuf = NM_STR_BUF_INIT(0, FALSE);
+        int                      prio;
+        int                      first_prio = 0;
+        const NMDnsConfigIPData *ip_data;
+        const CList *            head;
+        gboolean                 is_first = TRUE;
+
+        head = _mgr_get_ip_config_lst_head(self);
         c_list_for_each_entry (ip_data, head, ip_config_lst) {
             gboolean skip = FALSE;
 
@@ -1244,16 +1247,13 @@ _collect_resolv_conf_data(NMDnsManager *     self,
             } else if (first_prio < 0 && first_prio != prio)
                 skip = TRUE;
 
-            if (nm_ip_config_get_num_nameservers(ip_data->ip_config)) {
-                _LOGT(
-                    "config: %8d %-7s v%c %-5d %s: %s",
-                    prio,
-                    _config_type_to_string(ip_data->ip_config_type),
-                    nm_utils_addr_family_to_char(nm_ip_config_get_addr_family(ip_data->ip_config)),
-                    ip_data->data->ifindex,
-                    skip ? "<SKIP>" : "",
-                    get_nameserver_list(ip_data->ip_config, &tmp_gstring));
-            }
+            _LOGT("config: %8d %-7s v%c %-5d %s: %s",
+                  prio,
+                  _config_type_to_string(ip_data->ip_config_type),
+                  nm_utils_addr_family_to_char(nm_ip_config_get_addr_family(ip_data->ip_config)),
+                  ip_data->data->ifindex,
+                  skip ? "<SKIP>" : "",
+                  get_nameserver_list(ip_data->ip_config, &tmp_strbuf));
 
             if (!skip)
                 merge_one_ip_config(&rc, ip_data->data->ifindex, ip_data->ip_config);
@@ -1394,7 +1394,7 @@ _mgr_configs_data_construct(NMDnsManager *self)
     CList *                        head;
     int                            prev_priority = G_MININT;
 
-    head = _mgr_get_ip_configs_lst_head(self);
+    head = _mgr_get_ip_config_lst_head(self);
 
 #if NM_MORE_ASSERTS
     /* we call _mgr_configs_data_clear() at the end of update. We
@@ -1611,7 +1611,7 @@ _mgr_configs_data_clear(NMDnsManager *self)
     NMDnsConfigIPData *ip_data;
     CList *            head;
 
-    head = _mgr_get_ip_configs_lst_head(self);
+    head = _mgr_get_ip_config_lst_head(self);
     c_list_for_each_entry (ip_data, head, ip_config_lst) {
         nm_clear_g_free(&ip_data->domains.search);
         nm_clear_pointer(&ip_data->domains.reverse, g_strfreev);
@@ -1624,7 +1624,7 @@ _mgr_configs_data_clear(NMDnsManager *self)
 /*****************************************************************************/
 
 static gboolean
-update_dns(NMDnsManager *self, gboolean no_caching, GError **error)
+update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError **error)
 {
     NMDnsManagerPrivate *priv                = NM_DNS_MANAGER_GET_PRIVATE(self);
     const char *         nis_domain          = NULL;
@@ -1680,7 +1680,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, GError **error)
     if (priv->sd_resolve_plugin) {
         nm_dns_plugin_update(priv->sd_resolve_plugin,
                              global_config,
-                             _mgr_get_ip_configs_lst_head(self),
+                             _mgr_get_ip_config_lst_head(self),
                              priv->hostname,
                              NULL);
     }
@@ -1702,7 +1702,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, GError **error)
         _LOGD("update-dns: updating plugin %s", plugin_name);
         if (!nm_dns_plugin_update(plugin,
                                   global_config,
-                                  _mgr_get_ip_configs_lst_head(self),
+                                  _mgr_get_ip_config_lst_head(self),
                                   priv->hostname,
                                   &plugin_error)) {
             _LOGW("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message);
@@ -1815,8 +1815,8 @@ plugin_skip:;
                            NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED);
     }
 
-    /* signal that resolv.conf was changed */
-    if (do_update && result == SR_SUCCESS)
+    /* signal that DNS resolution configs were changed */
+    if ((do_update || caching || force_emit) && result == SR_SUCCESS)
         g_signal_emit(self, signals[CONFIG_CHANGED], 0);
 
     nm_clear_pointer(&priv->config_variant, g_variant_unref);
@@ -1839,7 +1839,7 @@ _ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsConfigI
 {
     _ASSERT_dns_config_ip_data(ip_data);
 
-    NM_DNS_MANAGER_GET_PRIVATE(ip_data->data->self)->ip_configs_lst_need_sort = TRUE;
+    NM_DNS_MANAGER_GET_PRIVATE(ip_data->data->self)->ip_config_lst_need_sort = TRUE;
 }
 
 gboolean
@@ -1851,7 +1851,6 @@ nm_dns_manager_set_ip_config(NMDnsManager *    self,
     NMDnsConfigIPData *  ip_data;
     NMDnsConfigData *    data;
     int                  ifindex;
-    NMDnsConfigIPData ** p_best;
 
     g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
     g_return_val_if_fail(NM_IS_IP_CONFIG(ip_config), FALSE);
@@ -1870,10 +1869,6 @@ nm_dns_manager_set_ip_config(NMDnsManager *    self,
     if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) {
         if (!ip_data)
             return FALSE;
-        if (priv->best_ip_config_4 == ip_data)
-            priv->best_ip_config_4 = NULL;
-        if (priv->best_ip_config_6 == ip_data)
-            priv->best_ip_config_6 = NULL;
         /* deleting a config doesn't invalidate the configs' sort order. */
         _dns_config_ip_data_free(ip_data);
         if (c_list_is_empty(&data->data_lst_head))
@@ -1904,27 +1899,13 @@ nm_dns_manager_set_ip_config(NMDnsManager *    self,
     else
         ip_data->ip_config_type = ip_config_type;
 
-    priv->ip_configs_lst_need_sort = TRUE;
-
-    p_best = NM_IS_IP4_CONFIG(ip_config) ? &priv->best_ip_config_4 : &priv->best_ip_config_6;
-
-    if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) {
-        /* Only one best-device per IP version is allowed */
-        if (*p_best != ip_data) {
-            if (*p_best)
-                (*p_best)->ip_config_type = NM_DNS_IP_CONFIG_TYPE_DEFAULT;
-            *p_best = ip_data;
-        }
-    } else {
-        if (*p_best == ip_data)
-            *p_best = NULL;
-    }
+    priv->ip_config_lst_need_sort = TRUE;
 
 changed:
     if (!priv->updates_queue) {
         gs_free_error GError *error = NULL;
 
-        if (!update_dns(self, FALSE, &error))
+        if (!update_dns(self, FALSE, FALSE, &error))
             _LOGW("could not commit DNS changes: %s", error->message);
     }
 
@@ -1965,7 +1946,7 @@ nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean s
     if (!priv->updates_queue) {
         gs_free_error GError *error = NULL;
 
-        if (!update_dns(self, FALSE, &error))
+        if (!update_dns(self, FALSE, FALSE, &error))
             _LOGW("could not commit DNS changes: %s", error->message);
     }
 }
@@ -2012,7 +1993,7 @@ nm_dns_manager_end_updates(NMDnsManager *self, const char *func)
 
     /* Commit all the outstanding changes */
     _LOGD("(%s): committing DNS changes (%d)", func, priv->updates_queue);
-    if (!update_dns(self, FALSE, &error))
+    if (!update_dns(self, FALSE, FALSE, &error))
         _LOGW("could not commit DNS changes: %s", error->message);
 
     memset(priv->prev_hash, 0, sizeof(priv->prev_hash));
@@ -2038,7 +2019,7 @@ nm_dns_manager_stop(NMDnsManager *self)
     if (priv->dns_touched && priv->plugin && NM_IS_DNS_DNSMASQ(priv->plugin)) {
         gs_free_error GError *error = NULL;
 
-        if (!update_dns(self, TRUE, &error))
+        if (!update_dns(self, TRUE, FALSE, &error))
             _LOGW("could not commit DNS changes on shutdown: %s", error->message);
 
         priv->dns_touched = FALSE;
@@ -2363,7 +2344,7 @@ config_changed_cb(NMConfig *          config,
                          | NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) {
         gs_free_error GError *error = NULL;
 
-        if (!update_dns(self, FALSE, &error))
+        if (!update_dns(self, FALSE, TRUE, &error))
             _LOGW("could not commit DNS changes: %s", error->message);
     }
 }
@@ -2440,7 +2421,7 @@ _get_config_variant(NMDnsManager *self)
 
     g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
 
-    head = _mgr_get_ip_configs_lst_head(self);
+    head = _mgr_get_ip_config_lst_head(self);
     c_list_for_each_entry (ip_data, head, ip_config_lst) {
         const NMIPConfig *ip_config = ip_data->ip_config;
         GVariantBuilder   entry_builder;
@@ -2546,13 +2527,13 @@ nm_dns_manager_init(NMDnsManager *self)
     _LOGT("creating...");
 
     c_list_init(&priv->configs_lst_head);
-    c_list_init(&priv->ip_configs_lst_head);
+    c_list_init(&priv->ip_config_lst_head);
 
     priv->config = g_object_ref(nm_config_get());
 
     G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDnsConfigData, ifindex) == 0);
     priv->configs_dict = g_hash_table_new_full(nm_pint_hash,
-                                               nm_pint_equals,
+                                               nm_pint_equal,
                                                (GDestroyNotify) _dns_config_data_free,
                                                NULL);
 
@@ -2584,10 +2565,7 @@ dispose(GObject *object)
     g_clear_object(&priv->sd_resolve_plugin);
     _clear_plugin(self);
 
-    priv->best_ip_config_4 = NULL;
-    priv->best_ip_config_6 = NULL;
-
-    c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_configs_lst_head, ip_config_lst)
+    c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst)
         _dns_config_ip_data_free(ip_data);
 
     nm_clear_pointer(&priv->configs_dict, g_hash_table_destroy);
@@ -2618,13 +2596,13 @@ static const NMDBusInterfaceInfoExtended interface_info_dns_manager = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DNS_MANAGER,
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "s", NM_DNS_MANAGER_MODE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RcManager",
-                                                             "s",
-                                                             NM_DNS_MANAGER_RC_MANAGER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Configuration",
-                                                             "aa{sv}",
-                                                             NM_DNS_MANAGER_CONFIGURATION), ), ),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "s", NM_DNS_MANAGER_MODE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RcManager",
+                                                           "s",
+                                                           NM_DNS_MANAGER_RC_MANAGER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Configuration",
+                                                           "aa{sv}",
+                                                           NM_DNS_MANAGER_CONFIGURATION), ), ),
 };
 
 static void
diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h
index 937ba62a..501085f7 100644
--- a/src/core/dns/nm-dns-manager.h
+++ b/src/core/dns/nm-dns-manager.h
@@ -11,6 +11,7 @@
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 #include "nm-setting-connection.h"
+#include "nm-dns-plugin.h"
 
 typedef enum {
     NM_DNS_IP_CONFIG_TYPE_REMOVED = -1,
@@ -148,7 +149,7 @@ typedef enum {
 
 void nm_dns_manager_stop(NMDnsManager *self);
 
-gboolean nm_dns_manager_has_systemd_resolved(NMDnsManager *self);
+NMDnsPlugin *nm_dns_manager_get_systemd_resolved(NMDnsManager *self);
 
 /*****************************************************************************/
 
diff --git a/src/core/dns/nm-dns-plugin.c b/src/core/dns/nm-dns-plugin.c
index 74d4eb2b..e5035f06 100644
--- a/src/core/dns/nm-dns-plugin.c
+++ b/src/core/dns/nm-dns-plugin.c
@@ -12,7 +12,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 
 /*****************************************************************************/
diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h
index 644d01e5..82ca7d6b 100644
--- a/src/core/dns/nm-dns-plugin.h
+++ b/src/core/dns/nm-dns-plugin.h
@@ -6,7 +6,8 @@
 #ifndef __NM_DNS_PLUGIN_H__
 #define __NM_DNS_PLUGIN_H__
 
-#include "nm-dns-manager.h"
+#include "c-list/src/c-list.h"
+
 #include "nm-config-data.h"
 
 #define NM_TYPE_DNS_PLUGIN (nm_dns_plugin_get_type())
diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c
index f6e116f3..4b8c4f94 100644
--- a/src/core/dns/nm-dns-systemd-resolved.c
+++ b/src/core/dns/nm-dns-systemd-resolved.c
@@ -16,10 +16,10 @@
 #include <sys/stat.h>
 #include <linux/if.h>
 
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -28,7 +28,7 @@
 #include "nm-setting-connection.h"
 #include "devices/nm-device.h"
 #include "NetworkManagerUtils.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 
 #define SYSTEMD_RESOLVED_DBUS_SERVICE  "org.freedesktop.resolve1"
 #define SYSTEMD_RESOLVED_MANAGER_IFACE "org.freedesktop.resolve1.Manager"
@@ -52,19 +52,40 @@ typedef struct {
     int                   ifindex;
 } RequestItem;
 
+struct _NMDnsSystemdResolvedResolveHandle {
+    CList                 handle_lst;
+    NMDnsSystemdResolved *self;
+    GSource *             timeout_source;
+    GCancellable *        handle_cancellable;
+    gpointer              callback_user_data;
+    guint                 timeout_msec;
+    bool                  is_failing_on_idle;
+    union {
+        struct {
+            NMDnsSystemdResolvedResolveAddressCallback callback;
+            guint64                                    flags;
+            int                                        ifindex;
+            int                                        addr_family;
+            NMIPAddr                                   addr;
+        } r_address;
+    };
+};
+
 /*****************************************************************************/
 
 typedef struct {
     GDBusConnection *dbus_connection;
     GHashTable *     dirty_interfaces;
     GCancellable *   cancellable;
+    GSource *        try_start_timeout_source;
     CList            request_queue_lst_head;
+    char *           dbus_owner;
+    CList            handle_lst_head;
     guint            name_owner_changed_id;
     bool             send_updates_warn_ratelimited : 1;
     bool             try_start_blocked : 1;
-    bool             dbus_has_owner : 1;
     bool             dbus_initied : 1;
-    bool             request_queue_to_send : 1;
+    bool             send_updates_waiting : 1;
     NMTernary        has_link_default_route : 3;
 } NMDnsSystemdResolvedPrivate;
 
@@ -84,9 +105,39 @@ G_DEFINE_TYPE(NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN)
 
 /*****************************************************************************/
 
-#define _NMLOG_DOMAIN LOGD_DNS
+#define _NMLOG_DOMAIN      LOGD_DNS
+#define _NMLOG_PREFIX_NAME "dns-sd-resolved"
+
 #define _NMLOG(level, ...) \
-    __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, "dns-sd-resolved", __VA_ARGS__)
+    __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, _NMLOG_PREFIX_NAME, __VA_ARGS__)
+
+#define _NMLOG2(level, handle, ...)                                                         \
+    G_STMT_START                                                                            \
+    {                                                                                       \
+        const NMLogLevel _level = (level);                                                  \
+                                                                                            \
+        if (nm_logging_enabled(_level, (_NMLOG_DOMAIN))) {                                  \
+            const NMDnsSystemdResolvedResolveHandle *const _handle = (handle);              \
+                                                                                            \
+            _nm_log(_level,                                                                 \
+                    (_NMLOG_DOMAIN),                                                        \
+                    0,                                                                      \
+                    NULL,                                                                   \
+                    NULL,                                                                   \
+                    "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]: request[" NM_HASH_OBFUSCATE_PTR_FMT \
+                    "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                               \
+                    _NMLOG_PREFIX_NAME,                                                     \
+                    NM_HASH_OBFUSCATE_PTR(self),                                            \
+                    NM_HASH_OBFUSCATE_PTR(_handle) _NM_UTILS_MACRO_REST(__VA_ARGS__));      \
+        }                                                                                   \
+    }                                                                                       \
+    G_STMT_END
+
+/*****************************************************************************/
+
+static void _resolve_complete_error(NMDnsSystemdResolvedResolveHandle *handle, GError *error);
+
+static void _resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResolveHandle *handle);
 
 /*****************************************************************************/
 
@@ -201,10 +252,7 @@ update_add_ip_config(NMDnsSystemdResolved *self,
         g_variant_builder_add(dns, "i", addr_family);
         g_variant_builder_add_value(
             dns,
-            g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
-                                      nm_ip_config_get_nameserver(data->ip_config, i),
-                                      addr_size,
-                                      1));
+            nm_g_variant_new_ay(nm_ip_config_get_nameserver(data->ip_config, i), addr_size));
         g_variant_builder_close(dns);
         has_config = TRUE;
     }
@@ -326,57 +374,105 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic)
     return has_config;
 }
 
-static void
-send_updates(NMDnsSystemdResolved *self)
+static gboolean
+_ensure_resolved_running_timeout(gpointer user_data)
 {
-    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
-    RequestItem *                request_item;
+    NMDnsSystemdResolved *             self = user_data;
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolvedResolveHandle *handle;
 
-    if (!priv->request_queue_to_send) {
-        /* nothing to do. */
-        return;
-    }
+    nm_clear_g_source_inst(&priv->try_start_timeout_source);
 
-    if (!priv->dbus_initied) {
-        _LOGT("send-updates: D-Bus connection not ready");
-        return;
+    _LOGT("timeout waiting to D-Bus activate systemd-resolved. Systemd-resolved won't be "
+          "used until it appears on the bus");
+
+again:
+    c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) {
+        gs_free_error GError *error = NULL;
+
+        if (handle->is_failing_on_idle)
+            continue;
+
+        nm_utils_error_set_literal(&error,
+                                   NM_UTILS_ERROR_NOT_READY,
+                                   "timeout waiting for systemd-resolved to start");
+        _resolve_complete_error(handle, error);
+        goto again;
     }
 
-    if (!priv->dbus_has_owner) {
+    return G_SOURCE_CONTINUE;
+}
+
+static NMTernary
+ensure_resolved_running(NMDnsSystemdResolved *self)
+{
+    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+
+    if (!priv->dbus_initied)
+        return NM_TERNARY_DEFAULT;
+
+    if (!priv->dbus_owner) {
         if (priv->try_start_blocked) {
             /* we have no name owner and we already tried poking the service to
              * autostart. */
-            _LOGT("send-updates: no name owner");
-            return;
+            return NM_TERNARY_FALSE;
         }
 
-        _LOGT("send-updates: no name owner. Try start service...");
+        _LOGT("try D-Bus activating systemd-resolved...");
         priv->try_start_blocked = TRUE;
 
+        priv->try_start_timeout_source =
+            nm_g_source_attach(nm_g_timeout_source_new(4000,
+                                                       G_PRIORITY_DEFAULT,
+                                                       _ensure_resolved_running_timeout,
+                                                       self,
+                                                       NULL),
+                               NULL);
+
         nm_dbus_connection_call_start_service_by_name(priv->dbus_connection,
                                                       SYSTEMD_RESOLVED_DBUS_SERVICE,
                                                       -1,
                                                       NULL,
                                                       NULL,
                                                       NULL);
+        return NM_TERNARY_DEFAULT;
+    }
+
+    return NM_TERNARY_TRUE;
+}
+
+static void
+send_updates(NMDnsSystemdResolved *self)
+{
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    RequestItem *                      request_item;
+    NMDnsSystemdResolvedResolveHandle *handle;
+
+    if (!priv->send_updates_waiting) {
+        /* nothing to do. */
         return;
     }
 
+    if (ensure_resolved_running(self) != NM_TERNARY_TRUE)
+        return;
+
     nm_clear_g_cancellable(&priv->cancellable);
 
     if (c_list_is_empty(&priv->request_queue_lst_head)) {
         _LOGT("send-updates: no requests to send");
-        priv->request_queue_to_send = FALSE;
-        return;
+        priv->send_updates_waiting = FALSE;
+        goto start_resolve;
     }
 
-    _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head));
-
     priv->cancellable = g_cancellable_new();
 
-    priv->request_queue_to_send = FALSE;
+    priv->send_updates_waiting = FALSE;
+
+    _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head));
 
     c_list_for_each_entry (request_item, &priv->request_queue_lst_head, request_queue_lst) {
+        gs_free char *ss = NULL;
+
         if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE
             && priv->has_link_default_route == NM_TERNARY_FALSE) {
             /* The "SetLinkDefaultRoute" API is only supported since v240.
@@ -386,16 +482,12 @@ send_updates(NMDnsSystemdResolved *self)
             continue;
         }
 
-        /* Above we explicitly call "StartServiceByName" trying to avoid D-Bus activating systmd-resolved
-         * multiple times. There is still a race, were we might hit this line although actually
-         * the service just quit this very moment. In that case, we would try to D-Bus activate the
-         * service multiple times during each call (something we wanted to avoid).
-         *
-         * But this is hard to avoid, because we'd have to check the error failure to detect the reason
-         * and retry. The race is not critical, because at worst it results in logging a warning
-         * about failure to start systemd.resolved. */
+        _LOGT("send-updates: %s ( %s )",
+              request_item->operation,
+              (ss = g_variant_print(request_item->argument, FALSE)));
+
         g_dbus_connection_call(priv->dbus_connection,
-                               SYSTEMD_RESOLVED_DBUS_SERVICE,
+                               priv->dbus_owner,
                                SYSTEMD_RESOLVED_DBUS_PATH,
                                SYSTEMD_RESOLVED_MANAGER_IFACE,
                                request_item->operation,
@@ -407,6 +499,15 @@ send_updates(NMDnsSystemdResolved *self)
                                call_done,
                                request_item);
     }
+
+start_resolve:
+    c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) {
+        if (handle->handle_cancellable)
+            continue;
+        if (handle->is_failing_on_idle)
+            continue;
+        _resolve_start(self, handle);
+    }
 }
 
 static gboolean
@@ -479,7 +580,7 @@ update(NMDnsPlugin *            plugin,
         }
     }
 
-    priv->request_queue_to_send = TRUE;
+    priv->send_updates_waiting = TRUE;
     send_updates(self);
     return TRUE;
 }
@@ -498,10 +599,13 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner)
     else
         _LOGT("D-Bus name for systemd-resolved has owner %s", owner);
 
-    priv->dbus_has_owner = !!owner;
+    nm_clear_g_source_inst(&priv->try_start_timeout_source);
+
+    nm_utils_strdup_reset(&priv->dbus_owner, owner);
+
     if (owner) {
-        priv->try_start_blocked     = FALSE;
-        priv->request_queue_to_send = TRUE;
+        priv->try_start_blocked    = FALSE;
+        priv->send_updates_waiting = TRUE;
     } else
         priv->has_link_default_route = NM_TERNARY_DEFAULT;
 
@@ -531,6 +635,7 @@ name_owner_changed_cb(GDBusConnection *connection,
          * returns. */
         priv->dbus_initied = TRUE;
         nm_clear_g_cancellable(&priv->cancellable);
+        _LOGT("D-Bus connection is ready");
     }
 
     name_owner_changed(user_data, new_owner);
@@ -551,6 +656,7 @@ get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data)
     g_clear_object(&priv->cancellable);
 
     priv->dbus_initied = TRUE;
+    _LOGT("D-Bus connection is ready");
 
     name_owner_changed(self, name_owner);
 }
@@ -566,7 +672,253 @@ nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self)
 
     priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
 
-    return priv->dbus_initied && (priv->dbus_has_owner || !priv->try_start_blocked);
+    return priv->dbus_initied && (priv->dbus_owner || !priv->try_start_blocked);
+}
+
+/*****************************************************************************/
+
+static void
+_resolve_complete(NMDnsSystemdResolvedResolveHandle *      handle,
+                  const NMDnsSystemdResolvedAddressResult *names,
+                  guint                                    names_len,
+                  guint64                                  flags,
+                  GError *                                 error)
+{
+    NMDnsSystemdResolved *       self;
+    NMDnsSystemdResolvedPrivate *priv;
+
+    g_return_if_fail(handle && NM_IS_DNS_SYSTEMD_RESOLVED(handle->self));
+
+    self = handle->self;
+    priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+
+    nm_assert(c_list_contains(&priv->handle_lst_head, &handle->handle_lst));
+
+    c_list_unlink(&handle->handle_lst);
+
+    handle->self = NULL;
+
+    nm_clear_g_source_inst(&handle->timeout_source);
+    nm_clear_g_cancellable(&handle->handle_cancellable);
+
+    handle->r_address
+        .callback(self, handle, names, names_len, flags, error, handle->callback_user_data);
+
+    nm_g_slice_free(handle);
+}
+
+static void
+_resolve_complete_error(NMDnsSystemdResolvedResolveHandle *handle, GError *error)
+{
+    NMDnsSystemdResolved *self = handle->self;
+
+    nm_assert(error);
+    _LOG2T(handle, "request failed: %s", error->message);
+    _resolve_complete(handle, NULL, 0, 0, error);
+}
+
+static void
+_resolve_handle_call_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    gs_unref_variant GVariant *v             = NULL;
+    gs_free_error GError *             error = NULL;
+    NMDnsSystemdResolvedResolveHandle *handle;
+    NMDnsSystemdResolved *             self;
+    GVariantIter *                     v_names_iter;
+    guint64                            v_flags;
+    int                                v_ifindex;
+    char *                             v_name;
+    gs_unref_array GArray *v_names = NULL;
+    gs_free char *         ss      = NULL;
+
+    v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    handle = user_data;
+    self   = handle->self;
+
+    if (error) {
+        gs_free char *remote_error = NULL;
+
+        remote_error = g_dbus_error_get_remote_error(error);
+        if (nm_streq0(remote_error, "org.freedesktop.DBus.Error.ServiceUnknown")) {
+            _LOG2T(handle, "request failed due to service stop. Retry");
+            g_clear_object(&handle->handle_cancellable);
+            _resolve_start(self, handle);
+            return;
+        }
+
+        _resolve_complete_error(handle, error);
+        return;
+    }
+
+    _LOG2T(handle, "request completed: %s", (ss = g_variant_print(v, FALSE)));
+
+    v_names = g_array_new(FALSE, FALSE, sizeof(NMDnsSystemdResolvedAddressResult));
+
+    G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDnsSystemdResolvedAddressResult, name) == 0);
+    g_array_set_clear_func(v_names, nm_indirect_g_free);
+
+    g_variant_get(v, "(a(is)t)", &v_names_iter, &v_flags);
+
+    while (g_variant_iter_next(v_names_iter, "(is)", &v_ifindex, &v_name)) {
+        NMDnsSystemdResolvedAddressResult *n;
+
+        n  = nm_g_array_append_new(v_names, NMDnsSystemdResolvedAddressResult);
+        *n = (NMDnsSystemdResolvedAddressResult){
+            .name    = g_steal_pointer(&v_name),
+            .ifindex = v_ifindex,
+        };
+    }
+    g_variant_iter_free(v_names_iter);
+
+    _resolve_complete(handle,
+                      &g_array_index(v_names, NMDnsSystemdResolvedAddressResult, 0),
+                      v_names->len,
+                      v_flags,
+                      NULL);
+}
+
+static gboolean
+_resolve_failing_on_idle(gpointer user_data)
+{
+    NMDnsSystemdResolvedResolveHandle *handle = user_data;
+    gs_free_error GError *error               = NULL;
+
+    nm_utils_error_set_literal(&error,
+                               NM_UTILS_ERROR_NOT_READY,
+                               "systemd-resolved is not available");
+    _resolve_complete_error(handle, error);
+    return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+_resolve_handle_timeout(gpointer user_data)
+{
+    NMDnsSystemdResolvedResolveHandle *handle = user_data;
+    gs_free_error GError *error               = NULL;
+
+    nm_utils_error_set_literal(&error, NM_UTILS_ERROR_UNKNOWN, "timeout for request");
+    _resolve_complete_error(handle, error);
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+_resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResolveHandle *handle)
+{
+    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMTernary                    is_running;
+
+    nm_assert(!handle->handle_cancellable);
+    nm_assert(!handle->is_failing_on_idle);
+
+    is_running = ensure_resolved_running(self);
+
+    if (is_running == NM_TERNARY_FALSE) {
+        /* Systemd-resolved is not is_running and shall not be used. We fail
+         * on an idle handler. */
+        _LOG2T(handle, "systemd-resolved not running. Failing on idle...");
+        nm_assert(!handle->timeout_source);
+        handle->is_failing_on_idle = TRUE;
+        handle->timeout_source     = nm_g_source_attach(
+            nm_g_idle_source_new(G_PRIORITY_DEFAULT, _resolve_failing_on_idle, handle, NULL),
+            NULL);
+        return;
+    }
+
+    if (!handle->timeout_source) {
+        handle->timeout_source = nm_g_source_attach(nm_g_timeout_source_new(handle->timeout_msec,
+                                                                            G_PRIORITY_DEFAULT,
+                                                                            _resolve_handle_timeout,
+                                                                            handle,
+                                                                            NULL),
+                                                    NULL);
+    }
+
+    if (is_running == NM_TERNARY_DEFAULT) {
+        /* we are D-Bus activating systemd-resolved. Wait for it... */
+        _LOG2T(handle, "waiting for systemd-resolved to start...");
+        return;
+    }
+
+    nm_assert(!priv->send_updates_waiting);
+
+    handle->handle_cancellable = g_cancellable_new();
+
+    _LOG2T(handle, "start D-Bus request...");
+    g_dbus_connection_call(priv->dbus_connection,
+                           priv->dbus_owner,
+                           SYSTEMD_RESOLVED_DBUS_PATH,
+                           SYSTEMD_RESOLVED_MANAGER_IFACE,
+                           "ResolveAddress",
+                           g_variant_new("(ii@ayt)",
+                                         handle->r_address.ifindex,
+                                         handle->r_address.addr_family,
+                                         nm_g_variant_new_ay_inaddr(handle->r_address.addr_family,
+                                                                    &handle->r_address.addr),
+                                         handle->r_address.flags),
+                           G_VARIANT_TYPE("(a(is)t)"),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           handle->timeout_msec + 1000u,
+                           handle->handle_cancellable,
+                           _resolve_handle_call_cb,
+                           handle);
+}
+
+NMDnsSystemdResolvedResolveHandle *
+nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved *                     self,
+                                        int                                        ifindex,
+                                        int                                        addr_family,
+                                        const NMIPAddr *                           addr,
+                                        guint64                                    flags,
+                                        guint                                      timeout_msec,
+                                        NMDnsSystemdResolvedResolveAddressCallback callback,
+                                        gpointer                                   user_data)
+{
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolvedResolveHandle *handle;
+    char                               addr_str[NM_UTILS_INET_ADDRSTRLEN];
+
+    g_return_val_if_fail(NM_IS_DNS_SYSTEMD_RESOLVED(self), NULL);
+    nm_assert_addr_family(addr_family);
+    nm_assert(addr);
+    nm_assert(callback);
+
+    handle  = g_slice_new(NMDnsSystemdResolvedResolveHandle);
+    *handle = (NMDnsSystemdResolvedResolveHandle){
+        .self               = self,
+        .timeout_msec       = timeout_msec,
+        .callback_user_data = user_data,
+        .r_address =
+            {
+                .ifindex     = ifindex,
+                .addr_family = addr_family,
+                .addr        = *addr,
+                .flags       = flags,
+                .callback    = callback,
+            },
+    };
+    c_list_link_tail(&priv->handle_lst_head, &handle->handle_lst);
+
+    _LOG2T(handle,
+           "resolve-address(ifindex=%d, %s, flags=%" G_GINT64_MODIFIER "x): new request",
+           handle->r_address.ifindex,
+           nm_utils_inet_ntop(handle->r_address.addr_family, &handle->r_address.addr, addr_str),
+           handle->r_address.flags);
+
+    _resolve_start(self, handle);
+
+    return handle;
+}
+
+void
+nm_dns_systemd_resolved_resolve_cancel(NMDnsSystemdResolvedResolveHandle *handle)
+{
+    gs_free_error GError *error = NULL;
+
+    nm_utils_error_set_cancelled(&error, FALSE, "NMDnsSystemdResolved");
+    _resolve_complete_error(handle, error);
 }
 
 /*****************************************************************************/
@@ -579,6 +931,7 @@ nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self)
     priv->has_link_default_route = NM_TERNARY_DEFAULT;
 
     c_list_init(&priv->request_queue_lst_head);
+    c_list_init(&priv->handle_lst_head);
     priv->dirty_interfaces = g_hash_table_new(nm_direct_hash, NULL);
 
     priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET);
@@ -611,8 +964,18 @@ nm_dns_systemd_resolved_new(void)
 static void
 dispose(GObject *object)
 {
-    NMDnsSystemdResolved *       self = NM_DNS_SYSTEMD_RESOLVED(object);
-    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolved *             self = NM_DNS_SYSTEMD_RESOLVED(object);
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolvedResolveHandle *handle;
+
+    while ((handle = c_list_first_entry(&priv->handle_lst_head,
+                                        NMDnsSystemdResolvedResolveHandle,
+                                        handle_lst))) {
+        gs_free_error GError *error = NULL;
+
+        nm_utils_error_set_cancelled(&error, TRUE, "NMDnsSystemdResolved");
+        _resolve_complete_error(handle, error);
+    }
 
     free_pending_updates(self);
 
@@ -620,10 +983,14 @@ dispose(GObject *object)
 
     nm_clear_g_cancellable(&priv->cancellable);
 
+    nm_clear_g_source_inst(&priv->try_start_timeout_source);
+
     g_clear_object(&priv->dbus_connection);
     nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_unref);
 
     G_OBJECT_CLASS(nm_dns_systemd_resolved_parent_class)->dispose(object);
+
+    nm_clear_g_free(&priv->dbus_owner);
 }
 
 static void
diff --git a/src/core/dns/nm-dns-systemd-resolved.h b/src/core/dns/nm-dns-systemd-resolved.h
index 4ab04ab4..9f973318 100644
--- a/src/core/dns/nm-dns-systemd-resolved.h
+++ b/src/core/dns/nm-dns-systemd-resolved.h
@@ -8,6 +8,7 @@
 #define __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__
 
 #include "nm-dns-plugin.h"
+#include "nm-dns-manager.h"
 
 #define NM_TYPE_DNS_SYSTEMD_RESOLVED (nm_dns_systemd_resolved_get_type())
 #define NM_DNS_SYSTEMD_RESOLVED(obj) \
@@ -30,4 +31,34 @@ NMDnsPlugin *nm_dns_systemd_resolved_new(void);
 
 gboolean nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self);
 
+/*****************************************************************************/
+
+typedef struct _NMDnsSystemdResolvedResolveHandle NMDnsSystemdResolvedResolveHandle;
+
+typedef struct {
+    const char *name;
+    int         ifindex;
+} NMDnsSystemdResolvedAddressResult;
+
+typedef void (*NMDnsSystemdResolvedResolveAddressCallback)(
+    NMDnsSystemdResolved *                   self,
+    NMDnsSystemdResolvedResolveHandle *      handle,
+    const NMDnsSystemdResolvedAddressResult *names,
+    guint                                    names_len,
+    guint64                                  flags,
+    GError *                                 error,
+    gpointer                                 user_data);
+
+NMDnsSystemdResolvedResolveHandle *
+nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved *                     self,
+                                        int                                        ifindex,
+                                        int                                        addr_family,
+                                        const NMIPAddr *                           addr,
+                                        guint64                                    flags,
+                                        guint                                      timeout_msec,
+                                        NMDnsSystemdResolvedResolveAddressCallback callback,
+                                        gpointer                                   user_data);
+
+void nm_dns_systemd_resolved_resolve_cancel(NMDnsSystemdResolvedResolveHandle *handle);
+
 #endif /* __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__ */
diff --git a/src/core/dnsmasq/nm-dnsmasq-manager.c b/src/core/dnsmasq/nm-dnsmasq-manager.c
index 82c002b0..e8697e81 100644
--- a/src/core/dnsmasq/nm-dnsmasq-manager.c
+++ b/src/core/dnsmasq/nm-dnsmasq-manager.c
@@ -17,7 +17,7 @@
 #include "nm-dnsmasq-utils.h"
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define CONFDIR NMCONFDIR "/dnsmasq-shared.d"
 
diff --git a/src/core/dnsmasq/nm-dnsmasq-utils.c b/src/core/dnsmasq/nm-dnsmasq-utils.c
index ea23a78d..c2791b9e 100644
--- a/src/core/dnsmasq/nm-dnsmasq-utils.c
+++ b/src/core/dnsmasq/nm-dnsmasq-utils.c
@@ -5,10 +5,11 @@
 
 #include "src/core/nm-default-daemon.h"
 
+#include "nm-dnsmasq-utils.h"
+
 #include <arpa/inet.h>
 
-#include "nm-dnsmasq-utils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
 
 gboolean
diff --git a/src/core/dnsmasq/nm-dnsmasq-utils.h b/src/core/dnsmasq/nm-dnsmasq-utils.h
index 57bbc4c4..1fabc9ba 100644
--- a/src/core/dnsmasq/nm-dnsmasq-utils.h
+++ b/src/core/dnsmasq/nm-dnsmasq-utils.h
@@ -6,7 +6,7 @@
 #ifndef __NETWORKMANAGER_DNSMASQ_UTILS_H__
 #define __NETWORKMANAGER_DNSMASQ_UTILS_H__
 
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 gboolean nm_dnsmasq_utils_get_range(const NMPlatformIP4Address *addr,
                                     char *                      out_first,
diff --git a/src/core/initrd/meson.build b/src/core/initrd/meson.build
deleted file mode 100644
index bd590e8c..00000000
--- a/src/core/initrd/meson.build
+++ /dev/null
@@ -1,31 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-libnmi_core = static_library(
-  'nmi-core',
-  sources: files(
-    'nmi-cmdline-reader.c',
-    'nmi-dt-reader.c',
-    'nmi-ibft-reader.c',
-  ),
-  dependencies: core_default_dep,
-  c_args: daemon_c_flags,
-)
-
-executable(
-  'nm-initrd-generator',
-  'nm-initrd-generator.c',
-  dependencies: core_default_dep,
-  c_args: daemon_c_flags,
-  link_with: [
-    libNetworkManagerBase,
-    libnmi_core,
-  ],
-  link_args: ldflags_linker_script_binary,
-  link_depends: linker_script_binary,
-  install: true,
-  install_dir: nm_libexecdir,
-)
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/src/core/initrd/nm-initrd-generator.c b/src/core/initrd/nm-initrd-generator.c
deleted file mode 100644
index 18c7258a..00000000
--- a/src/core/initrd/nm-initrd-generator.c
+++ /dev/null
@@ -1,226 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-#include "nm-core-utils.h"
-#include "nm-core-internal.h"
-#include "nm-keyfile-internal.h"
-#include "nm-initrd-generator.h"
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-config.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;
-    nm_auto_unref_keyfile GKeyFile *file  = NULL;
-    gs_free char *                  data  = NULL;
-    gs_free_error GError *error           = NULL;
-    gsize                 len;
-
-    if (!nm_connection_normalize(connection, NULL, NULL, &error))
-        goto err_out;
-
-    file = nm_keyfile_write(connection, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, &error);
-    if (file == NULL)
-        goto err_out;
-
-    data = g_key_file_to_data(file, &len, &error);
-    if (!data)
-        goto err_out;
-
-    if (connections_dir) {
-        gs_free char *filename      = NULL;
-        gs_free char *full_filename = NULL;
-
-        filename      = nm_keyfile_utils_create_filename(basename, TRUE);
-        full_filename = g_build_filename(connections_dir, filename, NULL);
-
-        if (!nm_utils_file_set_contents(full_filename, data, len, 0600, NULL, &error))
-            goto err_out;
-    } else
-        g_print("\n*** Connection '%s' ***\n\n%s", basename, data);
-
-    return;
-err_out:
-    g_print("%s\n", error->message);
-}
-
-#define DEFAULT_SYSFS_DIR       "/sys"
-#define DEFAULT_INITRD_DATA_DIR NMRUNDIR "/initrd"
-#define DEFAULT_RUN_CONFIG_DIR  NMRUNDIR "/conf.d"
-
-int
-main(int argc, char *argv[])
-{
-    GHashTable *       connections;
-    gs_free char *     connections_dir  = NULL;
-    gs_free char *     initrd_dir       = NULL;
-    gs_free char *     sysfs_dir        = NULL;
-    gs_free char *     run_config_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",
-         NM_KEYFILE_PATH_NAME_RUN},
-        {"initrd-data-dir",
-         'i',
-         0,
-         G_OPTION_ARG_FILENAME,
-         &initrd_dir,
-         "Output initrd data directory",
-         DEFAULT_INITRD_DATA_DIR},
-        {"sysfs-dir",
-         'd',
-         0,
-         G_OPTION_ARG_FILENAME,
-         &sysfs_dir,
-         "The sysfs mount point",
-         DEFAULT_SYSFS_DIR},
-        {"run-config-dir",
-         'r',
-         0,
-         G_OPTION_ARG_FILENAME,
-         &run_config_dir,
-         "Output config directory",
-         DEFAULT_RUN_CONFIG_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}};
-    nm_auto_free_option_context GOptionContext *option_context = NULL;
-    gs_free_error GError *error                                = NULL;
-    gs_free char *        hostname                             = NULL;
-    int                   errsv;
-    gint64                carrier_timeout_sec = 0;
-
-    option_context = g_option_context_new(
-        "-- [ip=...] [rd.route=...] [bridge=...] [bond=...] [team=...] [vlan=...] "
-        "[bootdev=...] [nameserver=...] [rd.peerdns=...] [rd.bootif=...] [BOOTIF=...] "
-        "[rd.znet=...] [rd.net.timeout.carrier=...] ... ");
-
-    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", error->message);
-        return 1;
-    }
-
-    if (!remaining) {
-        /* No arguments, no networking. Don't bother. */
-        return 0;
-    }
-
-    if (!connections_dir)
-        connections_dir = g_strdup(NM_KEYFILE_PATH_NAME_RUN);
-    if (!sysfs_dir)
-        sysfs_dir = g_strdup(DEFAULT_SYSFS_DIR);
-    if (!initrd_dir)
-        initrd_dir = g_strdup(DEFAULT_INITRD_DATA_DIR);
-    if (!run_config_dir)
-        run_config_dir = g_strdup(DEFAULT_RUN_CONFIG_DIR);
-
-    connections = nmi_cmdline_reader_parse(sysfs_dir,
-                                           (const char *const *) remaining,
-                                           &hostname,
-                                           &carrier_timeout_sec);
-
-    if (dump_to_stdout) {
-        nm_clear_g_free(&connections_dir);
-        nm_clear_g_free(&initrd_dir);
-        nm_clear_g_free(&run_config_dir);
-        if (hostname)
-            g_print("\n*** Hostname '%s' ***\n", hostname);
-        if (carrier_timeout_sec != 0)
-            g_print("\n*** Carrier Wait Timeout %" G_GINT64_FORMAT " sec ***\n",
-                    carrier_timeout_sec);
-    } else {
-        if (g_mkdir_with_parents(connections_dir, 0755) != 0) {
-            errsv = errno;
-            _LOGW(LOGD_CORE, "%s: %s", connections_dir, nm_strerror_native(errsv));
-            return 1;
-        }
-        if (g_mkdir_with_parents(initrd_dir, 0755) != 0) {
-            errsv = errno;
-            _LOGW(LOGD_CORE, "%s: %s", initrd_dir, nm_strerror_native(errsv));
-            return 1;
-        }
-        if (g_mkdir_with_parents(run_config_dir, 0755) != 0) {
-            errsv = errno;
-            _LOGW(LOGD_CORE, "%s: %s", run_config_dir, nm_strerror_native(errsv));
-            return 1;
-        }
-
-        if (hostname) {
-            gs_free char *hostname_file = NULL;
-            gs_free char *data          = NULL;
-
-            hostname_file = g_strdup_printf("%s/hostname", initrd_dir);
-            data          = g_strdup_printf("%s\n", hostname);
-
-            if (!g_file_set_contents(hostname_file, data, strlen(data), &error)) {
-                _LOGW(LOGD_CORE, "%s: %s", hostname_file, error->message);
-                return 1;
-            }
-        }
-        if (carrier_timeout_sec != 0) {
-            nm_auto_unref_keyfile GKeyFile *keyfile  = NULL;
-            gs_free char *                  filename = NULL;
-
-            keyfile = g_key_file_new();
-            g_key_file_set_list_separator(keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR);
-            filename = g_strdup_printf("%s/15-carrier-timeout.conf", run_config_dir);
-
-            g_key_file_set_value(keyfile,
-                                 NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE "-15-carrier-timeout",
-                                 NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE,
-                                 "*");
-            g_key_file_set_int64(keyfile,
-                                 NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE "-15-carrier-timeout",
-                                 NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT,
-                                 carrier_timeout_sec * 1000);
-
-            if (!g_key_file_save_to_file(keyfile, filename, &error)) {
-                _LOGW(LOGD_CORE, "%s: %s", filename, error->message);
-                return 1;
-            }
-        }
-    }
-
-    g_hash_table_foreach(connections, output_conn, connections_dir);
-    g_hash_table_destroy(connections);
-
-    return 0;
-}
diff --git a/src/core/initrd/nm-initrd-generator.h b/src/core/initrd/nm-initrd-generator.h
deleted file mode 100644
index 56dcfd68..00000000
--- a/src/core/initrd/nm-initrd-generator.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * 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"
-
-#define NMI_WAIT_DEVICE_TIMEOUT_MS 60000
-
-static inline int
-get_ip_address_family(const char *str, gboolean with_prefix)
-{
-    int addr_family;
-
-    if (!str)
-        return AF_UNSPEC;
-
-    if (with_prefix) {
-        if (nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, str, &addr_family, NULL, NULL))
-            return addr_family;
-    } else {
-        if (nm_utils_parse_inaddr_bin(AF_UNSPEC, str, &addr_family, NULL))
-            return addr_family;
-    }
-
-    return AF_UNSPEC;
-}
-
-GHashTable *nmi_ibft_read(const char *sysfs_dir);
-
-gboolean
-nmi_ibft_update_connection_from_nic(NMConnection *connection, GHashTable *nic, GError **error);
-
-NMConnection *nmi_dt_reader_parse(const char *sysfs_dir);
-
-GHashTable *nmi_cmdline_reader_parse(const char *       sysfs_dir,
-                                     const char *const *argv,
-                                     char **            hostname,
-                                     gint64 *           carrier_timeout_sec);
-
-#endif /* __NM_INITRD_GENERATOR_H__ */
diff --git a/src/core/initrd/nmi-cmdline-reader.c b/src/core/initrd/nmi-cmdline-reader.c
deleted file mode 100644
index 508ef2b2..00000000
--- a/src/core/initrd/nmi-cmdline-reader.c
+++ /dev/null
@@ -1,1269 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include <linux/if_ether.h>
-#include <linux/if_infiniband.h>
-
-#include "nm-core-internal.h"
-#include "nm-initrd-generator.h"
-#include "systemd/nm-sd-utils-shared.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__))
-
-/*****************************************************************************/
-
-typedef struct {
-    GHashTable *  hash;
-    GPtrArray *   array;
-    GPtrArray *   vlan_parents;
-    GHashTable *  explicit_ip_connections;
-    NMConnection *bootdev_connection; /* connection for bootdev=$ifname */
-    NMConnection *default_connection; /* connection not bound to any ifname */
-    char *        hostname;
-
-    /* Parameters to be set for all connections */
-    gboolean ignore_auto_dns;
-    int      dhcp_timeout;
-    char *   dhcp4_vci;
-
-    gint64 carrier_timeout_sec;
-} Reader;
-
-static Reader *
-reader_new(void)
-{
-    Reader *reader;
-
-    reader  = g_slice_new(Reader);
-    *reader = (Reader){
-        .hash = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_object_unref),
-        .explicit_ip_connections =
-            g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL),
-        .vlan_parents = g_ptr_array_new_with_free_func(g_free),
-        .array        = g_ptr_array_new(),
-    };
-
-    return reader;
-}
-
-static GHashTable *
-reader_destroy(Reader *reader, gboolean free_hash)
-{
-    gs_unref_hashtable GHashTable *hash = NULL;
-
-    g_ptr_array_unref(reader->array);
-    g_ptr_array_unref(reader->vlan_parents);
-    g_hash_table_unref(reader->explicit_ip_connections);
-    hash = g_steal_pointer(&reader->hash);
-    nm_clear_g_free(&reader->hostname);
-    nm_clear_g_free(&reader->dhcp4_vci);
-    nm_g_slice_free(reader);
-    if (!free_hash)
-        return g_steal_pointer(&hash);
-    return NULL;
-}
-
-static NMConnection *
-reader_add_connection(Reader *reader, const char *name, NMConnection *connection_take)
-{
-    char *name_dup;
-
-    name_dup = g_strdup(name);
-    if (g_hash_table_insert(reader->hash, name_dup, connection_take))
-        g_ptr_array_add(reader->array, name_dup);
-
-    return connection_take;
-}
-
-/* Returns a new connection owned by the reader */
-static NMConnection *
-reader_create_connection(Reader *                 reader,
-                         const char *             basename,
-                         const char *             id,
-                         const char *             ifname,
-                         const char *             mac,
-                         const char *             type_name,
-                         NMConnectionMultiConnect multi_connect)
-{
-    NMConnection *connection;
-    NMSetting *   setting;
-
-    connection = reader_add_connection(reader, basename, nm_simple_connection_new());
-
-    /* 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,
-                 NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,
-                 reader->ignore_auto_dns,
-                 NM_SETTING_IP_CONFIG_DHCP_TIMEOUT,
-                 reader->dhcp_timeout,
-                 NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER,
-                 reader->dhcp4_vci,
-                 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,
-                 NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
-                 (int) NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
-                 NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,
-                 reader->ignore_auto_dns,
-                 NM_SETTING_IP_CONFIG_DHCP_TIMEOUT,
-                 reader->dhcp_timeout,
-                 NULL);
-
-    setting = nm_setting_connection_new();
-    nm_connection_add_setting(connection, setting);
-    g_object_set(setting,
-                 NM_SETTING_CONNECTION_ID,
-                 id,
-                 NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
-                 NM_SETTING_CONNECTION_INTERFACE_NAME,
-                 ifname,
-                 NM_SETTING_CONNECTION_TYPE,
-                 type_name,
-                 NM_SETTING_CONNECTION_MULTI_CONNECT,
-                 multi_connect,
-                 NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
-                 1,
-                 NULL);
-
-    if (nm_streq0(type_name, NM_SETTING_INFINIBAND_SETTING_NAME)) {
-        setting = nm_setting_infiniband_new();
-        nm_connection_add_setting(connection, setting);
-        g_object_set(setting, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL);
-    }
-
-    if (mac) {
-        if (nm_streq0(type_name, NM_SETTING_INFINIBAND_SETTING_NAME)) {
-            setting = (NMSetting *) nm_connection_get_setting_infiniband(connection);
-            g_object_set(setting, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
-        } else {
-            setting = nm_setting_wired_new();
-            nm_connection_add_setting(connection, setting);
-            g_object_set(setting, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
-        }
-    }
-
-    return connection;
-}
-
-static NMConnection *
-reader_get_default_connection(Reader *reader)
-{
-    NMConnection *con;
-
-    if (!reader->default_connection) {
-        con = reader_create_connection(reader,
-                                       "default_connection",
-                                       "Wired Connection",
-                                       NULL,
-                                       NULL,
-                                       NM_SETTING_WIRED_SETTING_NAME,
-                                       NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
-        nm_connection_add_setting(con, nm_setting_wired_new());
-        reader->default_connection = con;
-    }
-    return reader->default_connection;
-}
-
-static NMConnection *
-reader_get_connection(Reader *    reader,
-                      const char *iface_spec,
-                      const char *type_name,
-                      gboolean    create_if_missing)
-{
-    NMConnection *connection = NULL;
-    NMSetting *   setting;
-    const char *  ifname = NULL;
-    gs_free char *mac    = NULL;
-
-    if (iface_spec) {
-        if (nm_utils_is_valid_iface_name(iface_spec, NULL))
-            ifname = iface_spec;
-        else {
-            mac = nm_utils_hwaddr_canonical(iface_spec, -1);
-            if (!mac)
-                _LOGW(LOGD_CORE, "invalid interface '%s'", iface_spec);
-        }
-    }
-
-    if (!ifname && !mac) {
-        NMConnection *       candidate;
-        NMSettingConnection *s_con;
-        guint                i;
-
-        /*
-         * If ifname was not given, we'll match the connection by type.
-         * If the type was not given either, then we're happy with any connection but slaves.
-         * This is so that things like "bond=bond0:eth1,eth2 nameserver=1.3.3.7 end up
-         * slapping the nameserver to the most reasonable connection (bond0).
-         */
-        for (i = 0; i < reader->array->len; i++) {
-            candidate = g_hash_table_lookup(reader->hash, reader->array->pdata[i]);
-            s_con     = nm_connection_get_setting_connection(candidate);
-
-            if (type_name == NULL && nm_setting_connection_get_master(s_con) == NULL) {
-                connection = candidate;
-                break;
-            }
-
-            if (type_name != NULL
-                && nm_streq(nm_setting_connection_get_connection_type(s_con), type_name)) {
-                connection = candidate;
-                break;
-            }
-        }
-    } else
-        connection = g_hash_table_lookup(reader->hash, (gpointer) ifname ?: mac);
-
-    if (!connection) {
-        if (!create_if_missing)
-            return NULL;
-
-        if (!type_name) {
-            if (NM_STR_HAS_PREFIX(ifname, "ib")
-                || (mac && nm_utils_hwaddr_valid(mac, INFINIBAND_ALEN)))
-                type_name = NM_SETTING_INFINIBAND_SETTING_NAME;
-            else
-                type_name = NM_SETTING_WIRED_SETTING_NAME;
-        }
-
-        connection = reader_create_connection(reader,
-                                              ifname ?: mac,
-                                              ifname ?: (mac ?: "Wired Connection"),
-                                              ifname,
-                                              mac,
-                                              type_name,
-                                              NM_CONNECTION_MULTI_CONNECT_SINGLE);
-    }
-    setting = (NMSetting *) nm_connection_get_setting_connection(connection);
-
-    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
-connection_set(NMConnection *connection,
-               const char *  setting_name,
-               const char *  property,
-               const char *  value)
-{
-    NMSetting *              setting;
-    GType                    setting_type;
-    nm_auto_unref_gtypeclass GObjectClass *object_class = NULL;
-    GParamSpec *                           spec;
-
-    setting_type = nm_setting_lookup_type(setting_name);
-    object_class = g_type_class_ref(setting_type);
-    spec         = g_object_class_find_property(object_class, property);
-    nm_assert(spec);
-
-    setting = nm_connection_get_setting_by_name(connection, setting_name);
-    if (!setting) {
-        setting = g_object_new(setting_type, NULL);
-        nm_connection_add_setting(connection, setting);
-    }
-
-    if (G_IS_PARAM_SPEC_UINT(spec)) {
-        guint v;
-
-        v = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT, 0);
-        if (errno || !nm_g_object_set_property_uint(G_OBJECT(setting), property, v, NULL)) {
-            _LOGW(LOGD_CORE,
-                  "Could not set property '%s.%s' to '%s'",
-                  setting_name,
-                  property,
-                  value);
-        }
-    } 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", property, setting_name);
-}
-
-static void
-reader_read_all_connections_from_fw(Reader *reader, const char *sysfs_dir)
-{
-    gs_unref_hashtable GHashTable *ibft = NULL;
-    NMConnection *                 dt_connection;
-    const char *                   mac;
-    GHashTable *                   nic;
-    const char *                   index;
-    GError *                       error = NULL;
-    guint                          i, length;
-    gs_free const char **          keys = NULL;
-
-    ibft = nmi_ibft_read(sysfs_dir);
-    keys = nm_utils_strdict_get_keys(ibft, TRUE, &length);
-
-    for (i = 0; i < length; i++) {
-        gs_unref_object NMConnection *connection = NULL;
-        gs_free char *                name       = NULL;
-
-        mac        = keys[i];
-        nic        = g_hash_table_lookup(ibft, mac);
-        connection = nm_simple_connection_new();
-        index      = g_hash_table_lookup(nic, "index");
-        if (!index) {
-            _LOGW(LOGD_CORE, "Ignoring an iBFT entry without an index");
-            continue;
-        }
-
-        if (!nmi_ibft_update_connection_from_nic(connection, nic, &error)) {
-            _LOGW(LOGD_CORE, "Unable to merge iBFT configuration: %s", error->message);
-            g_error_free(error);
-            continue;
-        }
-
-        name = g_strdup_printf("ibft%s", index);
-        reader_add_connection(reader, name, g_steal_pointer(&connection));
-    }
-
-    dt_connection = nmi_dt_reader_parse(sysfs_dir);
-    if (dt_connection)
-        reader_add_connection(reader, "ofw", dt_connection);
-}
-
-static void
-reader_parse_ip(Reader *reader, 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 *                   tmp2;
-    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 *                   iface_spec       = 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|link6|ibft} */
-        kind = tmp;
-    } else {
-        tmp2 = get_word(&argument, ':');
-        if (NM_IN_STRSET(tmp2,
-                         "none",
-                         "off",
-                         "dhcp",
-                         "on"
-                         "any",
-                         "dhcp6",
-                         "auto",
-                         "auto6",
-                         "link6",
-                         "ibft")) {
-            /* <ifname>:{none|off|dhcp|on|any|dhcp6|auto|auto6|link6|ibft} */
-            iface_spec = tmp;
-            kind       = tmp2;
-        } else {
-            /* <client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<kind> */
-            client_ip = tmp;
-            if (client_ip) {
-                client_ip_family = get_ip_address_family(client_ip, TRUE);
-                if (client_ip_family == AF_UNSPEC) {
-                    _LOGW(LOGD_CORE, "Invalid IP address '%s'.", client_ip);
-                    return;
-                }
-            }
-
-            peer            = tmp2;
-            gateway_ip      = get_word(&argument, ':');
-            netmask         = get_word(&argument, ':');
-            client_hostname = get_word(&argument, ':');
-            iface_spec      = get_word(&argument, ':');
-            kind            = get_word(&argument, ':');
-        }
-
-        if (client_hostname && !nm_sd_hostname_is_valid(client_hostname, FALSE))
-            client_hostname = NULL;
-
-        if (client_hostname) {
-            g_free(reader->hostname);
-            reader->hostname = g_strdup(client_hostname);
-        }
-
-        tmp                = get_word(&argument, ':');
-        dns_addr_family[0] = get_ip_address_family(tmp, FALSE);
-        if (dns_addr_family[0] != AF_UNSPEC) {
-            dns[0]             = tmp;
-            dns[1]             = get_word(&argument, ':');
-            dns_addr_family[1] = get_ip_address_family(dns[1], FALSE);
-            if (*argument)
-                _LOGW(LOGD_CORE, "Ignoring extra: '%s'.", argument);
-        } else {
-            mtu     = tmp;
-            macaddr = argument;
-        }
-    }
-
-    if (iface_spec == NULL && NM_IN_STRSET(kind, "fw", "ibft")) {
-        reader_read_all_connections_from_fw(reader, sysfs_dir);
-        return;
-    }
-
-    /* Parsing done, construct the NMConnection. */
-    if (iface_spec)
-        connection = reader_get_connection(reader, iface_spec, NULL, TRUE);
-    else
-        connection = reader_get_default_connection(reader);
-
-    g_hash_table_add(reader->explicit_ip_connections, g_object_ref(connection));
-
-    s_ip4 = nm_connection_get_setting_ip4_config(connection);
-    s_ip6 = nm_connection_get_setting_ip6_config(connection);
-
-    if (netmask && *netmask) {
-        gboolean is_ipv4 = client_ip_family == AF_INET;
-        NMIPAddr addr;
-
-        if (is_ipv4 && nm_utils_parse_inaddr_bin(AF_INET, netmask, NULL, &addr))
-            client_ip_prefix = nm_utils_ip4_netmask_to_prefix(addr.addr4);
-        else
-            client_ip_prefix = _nm_utils_ascii_str_to_int64(netmask, 10, 0, is_ipv4 ? 32 : 128, -1);
-
-        if (client_ip_prefix == -1)
-            _LOGW(LOGD_CORE, "Invalid IP mask: %s", netmask);
-    }
-
-    /* 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,
-                                             NULL,
-                                             &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", client_ip, error->message);
-                g_clear_error(&error);
-            }
-        } else
-            nm_assert_not_reached();
-
-        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:
-                nm_assert_not_reached();
-                break;
-            }
-            nm_ip_address_unref(address);
-        }
-    }
-
-    /* Dynamic IP configuration configured explicitly. */
-    if (NM_IN_STRSET(kind, "none", "off")) {
-        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_DISABLED,
-                         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 (nm_streq0(kind, "dhcp")) {
-        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_AUTO,
-                         NULL);
-        }
-    } else if (NM_IN_STRSET(kind, "auto6", "dhcp6")) {
-        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 (nm_streq0(kind, "link6")) {
-        g_object_set(s_ip6,
-                     NM_SETTING_IP_CONFIG_METHOD,
-                     NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
-                     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 (nm_streq0(kind, "ibft")) {
-        NMSettingWired *s_wired;
-        const char *    mac = NULL;
-        const char *    ifname;
-        gs_free char *  mac_free     = NULL;
-        gs_free char *  address_path = NULL;
-        GHashTable *    nic          = NULL;
-
-        if ((s_wired = nm_connection_get_setting_wired(connection))
-            && (mac = nm_setting_wired_get_mac_address(s_wired))) {
-            /* got mac from the connection */
-        } else if ((ifname = nm_connection_get_interface_name(connection))) {
-            /* read it from sysfs */
-            address_path = g_build_filename(sysfs_dir, "class", "net", ifname, "address", NULL);
-            if (g_file_get_contents(address_path, &mac_free, NULL, &error)) {
-                g_strchomp(mac_free);
-                mac = mac_free;
-            } else {
-                _LOGW(LOGD_CORE, "Can't get a MAC address for %s: %s", ifname, error->message);
-                g_clear_error(&error);
-            }
-        }
-
-        if (mac) {
-            gs_free char *mac_up = NULL;
-
-            mac_up = g_ascii_strup(mac, -1);
-            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)", iface_spec, mac_up);
-        }
-
-        if (nic) {
-            if (!nmi_ibft_update_connection_from_nic(connection, nic, &error)) {
-                _LOGW(LOGD_CORE, "Unable to merge iBFT configuration: %s", error->message);
-                g_clear_error(&error);
-            }
-        }
-    }
-
-    if (peer && *peer)
-        _LOGW(LOGD_CORE, "Ignoring peer: %s (not implemented)\n", peer);
-
-    if (gateway_ip && *gateway_ip) {
-        switch (get_ip_address_family(gateway_ip, FALSE)) {
-        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, "Invalid gateway: %s", gateway_ip);
-            break;
-        }
-    }
-
-    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_is_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", dns[i]);
-                break;
-            }
-        } else {
-            _LOGW(LOGD_CORE, "Invalid name server: %s", dns[i]);
-        }
-    }
-
-    if (mtu && *mtu)
-        connection_set(connection, NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MTU, mtu);
-
-    if (macaddr && *macaddr)
-        connection_set(connection,
-                       NM_SETTING_WIRED_SETTING_NAME,
-                       NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
-                       macaddr);
-}
-
-static void
-reader_parse_master(Reader *reader, char *argument, const char *type_name, const char *default_name)
-{
-    NMConnection *       connection;
-    NMSettingConnection *s_con;
-    gs_free char *       master_to_free = NULL;
-    const char *         master;
-    char *               slaves;
-    const char *         slave;
-    char *               opts;
-    const char *         mtu = NULL;
-
-    master = get_word(&argument, ':');
-    if (!master)
-        master = master_to_free = g_strdup_printf("%s0", default_name ?: type_name);
-    slaves = get_word(&argument, ':');
-
-    connection = reader_get_connection(reader, master, type_name, TRUE);
-    s_con      = nm_connection_get_setting_connection(connection);
-    master     = nm_setting_connection_get_uuid(s_con);
-
-    if (nm_streq(type_name, NM_SETTING_BRIDGE_SETTING_NAME)) {
-        NMSettingBridge *s_bridge = nm_connection_get_setting_bridge(connection);
-
-        /* Avoid the forwarding delay */
-        g_object_set(s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
-    } else if (nm_streq(type_name, NM_SETTING_BOND_SETTING_NAME)) {
-        NMSettingBond *s_bond = nm_connection_get_setting_bond(connection);
-
-        opts = get_word(&argument, ':');
-        while (opts && *opts) {
-            gs_free_error GError *error = NULL;
-            char *                opt;
-            const char *          opt_name;
-
-            opt      = get_word(&opts, ',');
-            opt_name = get_word(&opt, '=');
-
-            if (!_nm_setting_bond_validate_option(opt_name, opt, &error)) {
-                _LOGW(LOGD_CORE,
-                      "Ignoring invalid bond option: %s%s%s = %s%s%s: %s",
-                      NM_PRINT_FMT_QUOTE_STRING(opt_name),
-                      NM_PRINT_FMT_QUOTE_STRING(opt),
-                      error->message);
-                continue;
-            }
-            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 = reader_get_connection(reader, slave, NULL, TRUE);
-        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)
-            connection_set(connection, NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MTU, mtu);
-    } while (slaves && *slaves != '\0');
-
-    if (argument && *argument)
-        _LOGW(LOGD_CORE, "Ignoring extra: '%s'.", argument);
-}
-
-static void
-reader_add_routes(Reader *reader, GPtrArray *array)
-{
-    guint i;
-
-    for (i = 0; i < array->len; i++) {
-        NMConnection *     connection = NULL;
-        const char *       net;
-        const char *       gateway;
-        const char *       interface;
-        int                family       = AF_UNSPEC;
-        NMIPAddr           net_addr     = {};
-        NMIPAddr           gateway_addr = {};
-        int                net_prefix   = -1;
-        NMIPRoute *        route;
-        NMSettingIPConfig *s_ip;
-        char *             argument;
-        gs_free_error GError *error = NULL;
-
-        argument  = array->pdata[i];
-        net       = get_word(&argument, ':');
-        gateway   = get_word(&argument, ':');
-        interface = get_word(&argument, ':');
-
-        if (interface)
-            connection = reader_get_connection(reader, interface, NULL, TRUE);
-        if (!connection)
-            connection = reader->bootdev_connection;
-        if (!connection)
-            connection = reader_get_connection(reader, interface, NULL, FALSE);
-        if (!connection)
-            connection = reader_get_default_connection(reader);
-
-        if (net && *net) {
-            if (!nm_utils_parse_inaddr_prefix_bin(family, net, &family, &net_addr, &net_prefix)) {
-                _LOGW(LOGD_CORE, "Unrecognized address: %s", net);
-                continue;
-            }
-        }
-
-        if (gateway && *gateway) {
-            if (!nm_utils_parse_inaddr_bin(family, gateway, &family, &gateway_addr)) {
-                _LOGW(LOGD_CORE, "Unrecognized address: %s", gateway);
-                continue;
-            }
-        }
-
-        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", net);
-            continue;
-        }
-
-        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);
-            continue;
-        }
-
-        nm_setting_ip_config_add_route(s_ip, route);
-        nm_ip_route_unref(route);
-    }
-}
-
-static void
-reader_parse_vlan(Reader *reader, 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 = reader_get_connection(reader, vlan, NM_SETTING_VLAN_SETTING_NAME, TRUE);
-
-    s_vlan = nm_connection_get_setting_vlan(connection);
-    g_object_set(s_vlan,
-                 NM_SETTING_VLAN_PARENT,
-                 phy,
-                 NM_SETTING_VLAN_ID,
-                 (guint) _nm_utils_ascii_str_to_int64(vlanid, 10, 0, G_MAXUINT, G_MAXUINT),
-                 NULL);
-
-    if (argument && *argument)
-        _LOGW(LOGD_CORE, "Ignoring extra: '%s'.", argument);
-
-    if (!nm_strv_ptrarray_contains(reader->vlan_parents, phy))
-        g_ptr_array_add(reader->vlan_parents, g_strdup(phy));
-}
-
-static void
-reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames)
-{
-    const char *    nettype;
-    const char *    subchannels[4] = {0, 0, 0, 0};
-    const char *    tmp;
-    gs_free char *  ifname = NULL;
-    const char *    prefix;
-    NMConnection *  connection;
-    NMSettingWired *s_wired;
-    static int      count_ctc = 0;
-    static int      count_eth = 0;
-    int             index;
-
-    nettype        = get_word(&argument, ',');
-    subchannels[0] = get_word(&argument, ',');
-    subchannels[1] = get_word(&argument, ',');
-
-    if (nm_streq0(nettype, "ctc")) {
-        if (net_ifnames == TRUE) {
-            prefix = "sl";
-        } else {
-            prefix = "ctc";
-            index  = count_ctc++;
-        }
-    } else {
-        subchannels[2] = get_word(&argument, ',');
-        if (net_ifnames == TRUE) {
-            prefix = "en";
-        } else {
-            prefix = "eth";
-            index  = count_eth++;
-        }
-    }
-
-    if (net_ifnames == TRUE) {
-        const char *bus_id;
-        size_t      bus_id_len;
-        size_t      bus_id_start;
-
-        /* The following logic is taken from names_ccw() in systemd/src/udev/udev-builtin-net_id.c */
-        bus_id       = subchannels[0];
-        bus_id_len   = strlen(bus_id);
-        bus_id_start = strspn(bus_id, ".0");
-        bus_id += bus_id_start < bus_id_len ? bus_id_start : bus_id_len - 1;
-
-        ifname = g_strdup_printf("%sc%s", prefix, bus_id);
-    } else {
-        ifname = g_strdup_printf("%s%d", prefix, index);
-    }
-
-    connection = reader_get_connection(reader, ifname, NM_SETTING_WIRED_SETTING_NAME, FALSE);
-    if (!connection)
-        return;
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_object_set(s_wired,
-                 NM_SETTING_WIRED_S390_NETTYPE,
-                 nettype,
-                 NM_SETTING_WIRED_S390_SUBCHANNELS,
-                 &subchannels,
-                 NULL);
-
-    while ((tmp = get_word(&argument, ',')) != NULL) {
-        char *val;
-
-        val = strchr(tmp, '=');
-        if (val) {
-            gs_free char *key = NULL;
-
-            key    = g_strndup(tmp, val - tmp);
-            val[0] = '\0';
-            val++;
-            nm_setting_wired_add_s390_option(s_wired, key, val);
-        }
-    }
-}
-
-static void
-_normalize_conn(gpointer key, gpointer value, gpointer user_data)
-{
-    NMConnection *connection = value;
-
-    nm_connection_normalize(connection, NULL, NULL, NULL);
-}
-
-static void
-reader_add_nameservers(Reader *reader, GPtrArray *nameservers)
-{
-    NMConnection *     connection;
-    NMSettingIPConfig *s_ip;
-    GHashTableIter     iter;
-    int                addr_family;
-    const char *       ns;
-    guint              i;
-
-    for (i = 0; i < nameservers->len; i++) {
-        ns          = nameservers->pdata[i];
-        addr_family = get_ip_address_family(ns, FALSE);
-        if (addr_family == AF_UNSPEC) {
-            _LOGW(LOGD_CORE, "Unknown address family: %s", ns);
-            continue;
-        }
-
-        g_hash_table_iter_init(&iter, reader->hash);
-        while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &connection)) {
-            switch (addr_family) {
-            case AF_INET:
-                s_ip = nm_connection_get_setting_ip4_config(connection);
-                if (!NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip),
-                                  NM_SETTING_IP4_CONFIG_METHOD_AUTO,
-                                  NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
-                    continue;
-                break;
-            case AF_INET6:
-                s_ip = nm_connection_get_setting_ip6_config(connection);
-                if (!NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip),
-                                  NM_SETTING_IP6_CONFIG_METHOD_AUTO,
-                                  NM_SETTING_IP6_CONFIG_METHOD_DHCP,
-                                  NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
-                    continue;
-                break;
-            default:
-                nm_assert_not_reached();
-                continue;
-            }
-
-            nm_setting_ip_config_add_dns(s_ip, ns);
-        }
-    }
-}
-
-static void
-connection_set_needed(NMConnection *connection)
-{
-    NMSettingConnection *s_con;
-
-    s_con = nm_connection_get_setting_connection(connection);
-    if (!nm_streq0(nm_setting_connection_get_connection_type(s_con), NM_SETTING_WIRED_SETTING_NAME))
-        return;
-
-    g_object_set(s_con,
-                 NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT,
-                 (int) NMI_WAIT_DEVICE_TIMEOUT_MS,
-                 NULL);
-}
-
-static void
-connection_set_needed_cb(gpointer key, gpointer value, gpointer user_data)
-{
-    connection_set_needed(value);
-}
-
-GHashTable *
-nmi_cmdline_reader_parse(const char *       sysfs_dir,
-                         const char *const *argv,
-                         char **            hostname,
-                         gint64 *           carrier_timeout_sec)
-{
-    Reader *          reader;
-    const char *      tag;
-    gboolean          ignore_bootif          = FALSE;
-    gboolean          neednet                = FALSE;
-    gs_free char *    bootif_val             = NULL;
-    gs_free char *    bootdev                = NULL;
-    gboolean          net_ifnames            = TRUE;
-    gs_unref_ptrarray GPtrArray *nameservers = NULL;
-    gs_unref_ptrarray GPtrArray *routes      = NULL;
-    gs_unref_ptrarray GPtrArray *znets       = NULL;
-    int                          i;
-    guint64                      dhcp_timeout   = 90;
-    guint64                      dhcp_num_tries = 1;
-
-    reader = reader_new();
-
-    for (i = 0; argv[i]; i++) {
-        gs_free char *argument_clone = NULL;
-        char *        argument;
-
-        argument_clone = g_strdup(argv[i]);
-        argument       = argument_clone;
-
-        tag = get_word(&argument, '=');
-
-        if (nm_streq(tag, "net.ifnames"))
-            net_ifnames = !nm_streq(argument, "0");
-        else if (nm_streq(tag, "rd.peerdns"))
-            reader->ignore_auto_dns = !_nm_utils_ascii_str_to_bool(argument, TRUE);
-        else if (nm_streq(tag, "rd.net.timeout.dhcp")) {
-            if (nm_streq0(argument, "infinity")) {
-                dhcp_timeout = G_MAXINT32;
-            } else {
-                dhcp_timeout =
-                    _nm_utils_ascii_str_to_int64(argument, 10, 1, G_MAXINT32, dhcp_timeout);
-            }
-        } else if (nm_streq(tag, "rd.net.dhcp.retry")) {
-            dhcp_num_tries =
-                _nm_utils_ascii_str_to_int64(argument, 10, 1, G_MAXINT32, dhcp_num_tries);
-        } else if (nm_streq(tag, "rd.net.dhcp.vendor-class")) {
-            if (nm_utils_validate_dhcp4_vendor_class_id(argument, NULL))
-                nm_utils_strdup_reset(&reader->dhcp4_vci, argument);
-        } else if (nm_streq(tag, "rd.net.timeout.carrier")) {
-            reader->carrier_timeout_sec =
-                _nm_utils_ascii_str_to_int64(argument, 10, 0, G_MAXINT32, 0);
-        }
-    }
-
-    reader->dhcp_timeout = NM_CLAMP(dhcp_timeout * dhcp_num_tries, 1, G_MAXINT32);
-
-    for (i = 0; argv[i]; i++) {
-        gs_free char *argument_clone = NULL;
-        char *        argument;
-        char *        word;
-
-        argument_clone = g_strdup(argv[i]);
-        argument       = argument_clone;
-
-        tag = get_word(&argument, '=');
-        if (nm_streq(tag, "ip"))
-            reader_parse_ip(reader, sysfs_dir, argument);
-        else if (nm_streq(tag, "rd.route")) {
-            if (!routes)
-                routes = g_ptr_array_new_with_free_func(g_free);
-            g_ptr_array_add(routes, g_strdup(argument));
-        } else if (nm_streq(tag, "bridge"))
-            reader_parse_master(reader, argument, NM_SETTING_BRIDGE_SETTING_NAME, "br");
-        else if (nm_streq(tag, "bond"))
-            reader_parse_master(reader, argument, NM_SETTING_BOND_SETTING_NAME, NULL);
-        else if (nm_streq(tag, "team"))
-            reader_parse_master(reader, argument, NM_SETTING_TEAM_SETTING_NAME, NULL);
-        else if (nm_streq(tag, "vlan"))
-            reader_parse_vlan(reader, argument);
-        else if (nm_streq(tag, "bootdev")) {
-            g_free(bootdev);
-            bootdev = g_strdup(argument);
-        } else if (nm_streq(tag, "nameserver")) {
-            word = get_word(&argument, '\0');
-            if (word) {
-                if (!nameservers)
-                    nameservers = g_ptr_array_new_with_free_func(g_free);
-                g_ptr_array_add(nameservers, g_strdup(word));
-            }
-            if (argument && *argument)
-                _LOGW(LOGD_CORE, "Ignoring extra: '%s'.", argument);
-        } else if (nm_streq(tag, "rd.iscsi.ibft") && _nm_utils_ascii_str_to_bool(argument, TRUE))
-            reader_read_all_connections_from_fw(reader, sysfs_dir);
-        else if (nm_streq(tag, "rd.bootif"))
-            ignore_bootif = !_nm_utils_ascii_str_to_bool(argument, TRUE);
-        else if (nm_streq(tag, "rd.neednet"))
-            neednet = _nm_utils_ascii_str_to_bool(argument, TRUE);
-        else if (nm_streq(tag, "rd.znet")) {
-            if (!znets)
-                znets = g_ptr_array_new_with_free_func(g_free);
-            g_ptr_array_add(znets, g_strdup(argument));
-        } else if (g_ascii_strcasecmp(tag, "BOOTIF") == 0) {
-            nm_clear_g_free(&bootif_val);
-            bootif_val = g_strdup(argument);
-        }
-    }
-
-    for (i = 0; i < reader->vlan_parents->len; i++) {
-        NMConnection *     connection;
-        NMSettingIPConfig *s_ip;
-
-        /* Disable IP configuration for parent connections of VLANs,
-         * unless those interfaces were explicitly configured otherwise. */
-
-        connection = reader_get_connection(reader, reader->vlan_parents->pdata[i], NULL, TRUE);
-        if (!g_hash_table_contains(reader->explicit_ip_connections, connection)) {
-            s_ip = nm_connection_get_setting_ip4_config(connection);
-            if (s_ip) {
-                g_object_set(s_ip,
-                             NM_SETTING_IP_CONFIG_METHOD,
-                             NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
-                             NULL);
-            }
-
-            s_ip = nm_connection_get_setting_ip6_config(connection);
-            if (s_ip) {
-                g_object_set(s_ip,
-                             NM_SETTING_IP_CONFIG_METHOD,
-                             NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
-                             NULL);
-            }
-        }
-    }
-
-    if (ignore_bootif)
-        nm_clear_g_free(&bootif_val);
-    if (bootif_val) {
-        NMConnection *  connection;
-        NMSettingWired *s_wired;
-        const char *    bootif = bootif_val;
-        char            prefix[4];
-
-        if (!nm_utils_hwaddr_valid(bootif, ETH_ALEN)) {
-            strncpy(prefix, bootif, 3);
-            prefix[3] = '\0';
-
-            if (NM_IN_STRSET(prefix, "01-", "01:", "00-", "00:")
-                && nm_utils_hwaddr_valid(&bootif[3], ETH_ALEN)) {
-                /*
-                 * BOOTIF MAC address can be prefixed with a hardware type identifier.
-                 * "01" stays for "wired", "00" is also accepted as it means "undefined".
-                 * No others are known.
-                 */
-                bootif += 3;
-            }
-        }
-
-        connection = reader_get_connection(reader, NULL, NM_SETTING_WIRED_SETTING_NAME, FALSE);
-        if (!connection)
-            connection = reader_get_default_connection(reader);
-
-        s_wired = nm_connection_get_setting_wired(connection);
-
-        if (nm_connection_get_interface_name(connection)
-            || (nm_setting_wired_get_mac_address(s_wired)
-                && !nm_utils_hwaddr_matches(nm_setting_wired_get_mac_address(s_wired),
-                                            -1,
-                                            bootif,
-                                            -1))) {
-            connection = reader_create_connection(reader,
-                                                  "bootif_connection",
-                                                  "BOOTIF Connection",
-                                                  NULL,
-                                                  bootif,
-                                                  NM_SETTING_WIRED_SETTING_NAME,
-                                                  NM_CONNECTION_MULTI_CONNECT_SINGLE);
-        } else {
-            g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, bootif, NULL);
-        }
-    }
-
-    if (bootdev) {
-        NMConnection *connection;
-
-        connection                 = reader_get_connection(reader, bootdev, NULL, TRUE);
-        reader->bootdev_connection = connection;
-        connection_set_needed(connection);
-    }
-
-    if (neednet) {
-        if (g_hash_table_size(reader->hash) == 0) {
-            /* Make sure there's some connection. */
-            reader_get_default_connection(reader);
-        }
-
-        g_hash_table_foreach(reader->hash, connection_set_needed_cb, NULL);
-    }
-
-    if (routes)
-        reader_add_routes(reader, routes);
-
-    if (nameservers)
-        reader_add_nameservers(reader, nameservers);
-
-    if (znets) {
-        for (i = 0; i < znets->len; i++)
-            reader_parse_rd_znet(reader, znets->pdata[i], net_ifnames);
-    }
-
-    g_hash_table_foreach(reader->hash, _normalize_conn, NULL);
-
-    NM_SET_OUT(hostname, g_steal_pointer(&reader->hostname));
-
-    NM_SET_OUT(carrier_timeout_sec, reader->carrier_timeout_sec);
-
-    return reader_destroy(reader, FALSE);
-}
diff --git a/src/core/initrd/nmi-dt-reader.c b/src/core/initrd/nmi-dt-reader.c
deleted file mode 100644
index 63e4524b..00000000
--- a/src/core/initrd/nmi-dt-reader.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2019 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-initrd-generator.h"
-
-#include <arpa/inet.h>
-#include <linux/if_ether.h>
-
-#include "nm-core-internal.h"
-
-/*****************************************************************************/
-
-#define _NMLOG(level, domain, ...) \
-    nm_log((level),                \
-           (domain),               \
-           NULL,                   \
-           NULL,                   \
-           "dt-reader: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__) _NM_UTILS_MACRO_REST(__VA_ARGS__))
-
-/*****************************************************************************/
-
-static gboolean
-dt_get_property(const char *base,
-                const char *dev,
-                const char *prop,
-                char **     contents,
-                size_t *    length)
-{
-    gs_free char *filename      = g_build_filename(base, dev, prop, NULL);
-    gs_free_error GError *error = NULL;
-
-    if (!g_file_test(filename, G_FILE_TEST_EXISTS))
-        return FALSE;
-
-    if (!contents)
-        return TRUE;
-
-    if (!g_file_get_contents(filename, contents, length, &error)) {
-        _LOGW(LOGD_CORE, "%s: Can not read the %s property: %s", dev, prop, error->message);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-static NMIPAddress *
-dt_get_ipaddr_property(const char *base, const char *dev, const char *prop, int *family)
-{
-    gs_free char *buf = NULL;
-    size_t        len;
-    int           f;
-
-    if (!dt_get_property(base, dev, prop, &buf, &len))
-        return NULL;
-
-    f = nm_utils_addr_family_from_size(len);
-    if (f == AF_UNSPEC || (*family != AF_UNSPEC && *family != f)) {
-        _LOGW(LOGD_CORE, "%s: Address %s has unrecognized length (%zd)", dev, prop, len);
-        return NULL;
-    }
-
-    *family = f;
-    return nm_ip_address_new_binary(f, buf, 0, NULL);
-}
-
-static char *
-dt_get_hwaddr_property(const char *base, const char *dev, const char *prop)
-{
-    gs_free guint8 *buf = NULL;
-    size_t          len;
-
-    if (!dt_get_property(base, dev, prop, (char **) &buf, &len))
-        return NULL;
-
-    if (len != ETH_ALEN) {
-        _LOGW(LOGD_CORE, "%s: MAC address %s has unrecognized length (%zd)", dev, prop, len);
-        return NULL;
-    }
-
-    return g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
-                           buf[0],
-                           buf[1],
-                           buf[2],
-                           buf[3],
-                           buf[4],
-                           buf[4]);
-}
-
-static NMIPAddress *
-str_addr(const char *str, int *family)
-{
-    NMIPAddr addr_bin;
-
-    if (!nm_utils_parse_inaddr_bin_full(*family, TRUE, str, family, &addr_bin)) {
-        _LOGW(LOGD_CORE, "Malformed IP address: '%s'", str);
-        return NULL;
-    }
-    return nm_ip_address_new_binary(*family, &addr_bin, 0, NULL);
-}
-
-NMConnection *
-nmi_dt_reader_parse(const char *sysfs_dir)
-{
-    gs_unref_object NMConnection *connection           = NULL;
-    gs_free char *                base                 = NULL;
-    gs_free char *                bootpath             = NULL;
-    gs_strfreev char **           tokens               = NULL;
-    char *                        path                 = NULL;
-    gboolean                      bootp                = FALSE;
-    const char *                  s_ipaddr             = NULL;
-    const char *                  s_netmask            = NULL;
-    const char *                  s_gateway            = NULL;
-    nm_auto_unref_ip_address NMIPAddress *ipaddr       = NULL;
-    nm_auto_unref_ip_address NMIPAddress *gateway      = NULL;
-    const char *                          duplex       = NULL;
-    gs_free char *                        hwaddr       = NULL;
-    gs_free char *                        local_hwaddr = NULL;
-    gs_free char *                        hostname     = NULL;
-    guint32                               speed        = 0;
-    int                                   prefix       = -1;
-    NMSettingIPConfig *                   s_ip         = NULL;
-    NMSetting *                           s_ip4        = NULL;
-    NMSetting *                           s_ip6        = NULL;
-    NMSetting *                           s_wired      = NULL;
-    int                                   family       = AF_UNSPEC;
-    int                                   i            = 0;
-    char *                                c;
-    gs_free_error GError *error = NULL;
-
-    base = g_build_filename(sysfs_dir, "firmware", "devicetree", "base", NULL);
-
-    if (!dt_get_property(base, "chosen", "bootpath", &bootpath, NULL))
-        return NULL;
-
-    c = strchr(bootpath, ':');
-    if (c) {
-        *c   = '\0';
-        path = c + 1;
-    } else {
-        path = "";
-    }
-
-    dt_get_property(base, "chosen", "client-name", &hostname, NULL);
-
-    local_hwaddr = dt_get_hwaddr_property(base, bootpath, "local-mac-address");
-    hwaddr       = dt_get_hwaddr_property(base, bootpath, "mac-address");
-    if (nm_streq0(local_hwaddr, hwaddr))
-        nm_clear_g_free(&local_hwaddr);
-
-    tokens = g_strsplit(path, ",", 0);
-
-    /*
-     * Ethernet device settings. Defined by "Open Firmware,
-     * Recommended Practice: Device Support Extensions, Version 1.0 [1]
-     * [1] https://www.devicetree.org/open-firmware/practice/devicex/dse1_0a.ps
-     */
-
-    for (i = 0; tokens[i]; i++) {
-        /* Skip these. They have magical meaning for OpenFirmware. */
-        if (NM_IN_STRSET(tokens[i], "nfs", "last"))
-            continue;
-        if (nm_streq(tokens[i], "promiscuous")) {
-            /* Ignore. */
-            continue;
-        }
-
-        if (g_str_has_prefix(tokens[i], "speed=")) {
-            speed = _nm_utils_ascii_str_to_int64(tokens[i] + 6, 10, 0, G_MAXUINT32, 0);
-            continue;
-        }
-
-        if (g_str_has_prefix(tokens[i], "duplex=auto")) {
-            continue;
-        } else if (g_str_has_prefix(tokens[i], "duplex=half")
-                   || g_str_has_prefix(tokens[i], "duplex=full")) {
-            duplex = tokens[i] + 7;
-            continue;
-        }
-
-        break;
-    }
-
-    /*
-     * Network boot configuration. Defined by "Open Firmware,
-     * Recommended Practice: TFTP Booting Extension, Version 1.0 [1]
-     * [1] https://www.devicetree.org/open-firmware/practice/obp-tftp/tftp1_0.pdf
-     */
-
-    for (; tokens[i]; i++) {
-        if (NM_IN_STRSET(tokens[i], "bootp", "dhcp", "rarp")) {
-            bootp = TRUE;
-            continue;
-        }
-        break;
-    }
-
-    /* s-iaddr, or perhaps a raw absolute filename */
-    if (tokens[i] && tokens[i][0] != '/')
-        i++;
-
-    /* filename */
-    if (tokens[i])
-        i++;
-
-    /* c-iaddr */
-    if (tokens[i]) {
-        s_ipaddr = tokens[i];
-        i++;
-    }
-
-    /* g-iaddr */
-    if (tokens[i]) {
-        s_gateway = tokens[i];
-        i++;
-    }
-
-    if (tokens[i] && (strchr(tokens[i], '.') || strchr(tokens[i], ':'))) {
-        /* yaboot claims the mask can be specified here,
-         * though it doesn't support it. */
-        s_netmask = tokens[i];
-        i++;
-    }
-
-    /* bootp-retries */
-    if (tokens[i])
-        i++;
-
-    /* tftp-retries */
-    if (tokens[i])
-        i++;
-
-    if (tokens[i]) {
-        /* yaboot accepts a mask here */
-        s_netmask = tokens[i];
-        i++;
-    }
-
-    connection = nm_simple_connection_new();
-
-    nm_connection_add_setting(connection,
-                              g_object_new(NM_TYPE_SETTING_CONNECTION,
-                                           NM_SETTING_CONNECTION_TYPE,
-                                           NM_SETTING_WIRED_SETTING_NAME,
-                                           NM_SETTING_CONNECTION_ID,
-                                           "OpenFirmware Connection",
-                                           NULL));
-
-    s_ip4 = nm_setting_ip4_config_new();
-    nm_connection_add_setting(connection, s_ip4);
-
-    s_ip6 = nm_setting_ip6_config_new();
-    nm_connection_add_setting(connection, s_ip6);
-
-    g_object_set(s_ip6,
-                 NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
-                 (int) NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
-                 NULL);
-
-    if (!bootp && dt_get_property(base, "chosen", "bootp-response", NULL, NULL))
-        bootp = TRUE;
-
-    if (!bootp) {
-        nm_auto_unref_ip_address NMIPAddress *netmask = NULL;
-
-        netmask = dt_get_ipaddr_property(base, "chosen", "netmask-ip", &family);
-        gateway = dt_get_ipaddr_property(base, "chosen", "gateway-ip", &family);
-        if (gateway)
-            s_gateway = nm_ip_address_get_address(gateway);
-        ipaddr = dt_get_ipaddr_property(base, "chosen", "client-ip", &family);
-
-        if (family == AF_UNSPEC) {
-            nm_assert(netmask == NULL);
-            nm_assert(gateway == NULL);
-            nm_assert(ipaddr == NULL);
-
-            netmask = str_addr(s_netmask, &family);
-            ipaddr  = str_addr(s_ipaddr, &family);
-
-            prefix = _nm_utils_ascii_str_to_int64(s_netmask, 10, 0, 128, -1);
-        }
-
-        if (prefix == -1 && family == AF_INET && netmask) {
-            guint32 netmask_v4;
-
-            nm_ip_address_get_address_binary(netmask, &netmask_v4);
-            prefix = nm_utils_ip4_netmask_to_prefix(netmask_v4);
-        }
-
-        if (prefix == -1)
-            _LOGW(LOGD_CORE, "Unable to determine the network prefix");
-        else
-            nm_ip_address_set_prefix(ipaddr, prefix);
-    }
-
-    if (!ipaddr) {
-        family = AF_UNSPEC;
-        bootp  = TRUE;
-    }
-
-    if (bootp) {
-        g_object_set(s_ip4,
-                     NM_SETTING_IP_CONFIG_METHOD,
-                     NM_SETTING_IP4_CONFIG_METHOD_AUTO,
-                     NM_SETTING_IP_CONFIG_DHCP_HOSTNAME,
-                     hostname,
-                     NULL);
-        g_object_set(s_ip6,
-                     NM_SETTING_IP_CONFIG_METHOD,
-                     NM_SETTING_IP6_CONFIG_METHOD_AUTO,
-                     NM_SETTING_IP_CONFIG_DHCP_HOSTNAME,
-                     hostname,
-                     NULL);
-    } else {
-        switch (family) {
-        case AF_INET:
-            s_ip = (NMSettingIPConfig *) s_ip4;
-            g_object_set(s_ip4,
-                         NM_SETTING_IP_CONFIG_METHOD,
-                         NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
-                         NULL);
-            g_object_set(s_ip6,
-                         NM_SETTING_IP_CONFIG_METHOD,
-                         NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
-                         NULL);
-            break;
-        case AF_INET6:
-            s_ip = (NMSettingIPConfig *) s_ip6;
-            g_object_set(s_ip4,
-                         NM_SETTING_IP_CONFIG_METHOD,
-                         NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
-                         NULL);
-            g_object_set(s_ip6,
-                         NM_SETTING_IP_CONFIG_METHOD,
-                         NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
-                         NULL);
-            break;
-        default:
-            g_return_val_if_reached(NULL);
-        }
-
-        nm_setting_ip_config_add_address(s_ip, ipaddr);
-        g_object_set(s_ip, NM_SETTING_IP_CONFIG_GATEWAY, s_gateway, NULL);
-    }
-
-    if (duplex || speed || hwaddr || local_hwaddr) {
-        s_wired = nm_setting_wired_new();
-        nm_connection_add_setting(connection, s_wired);
-
-        g_object_set(s_wired,
-                     NM_SETTING_WIRED_SPEED,
-                     speed,
-                     NM_SETTING_WIRED_DUPLEX,
-                     duplex,
-                     NM_SETTING_WIRED_MAC_ADDRESS,
-                     hwaddr,
-                     NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
-                     local_hwaddr,
-                     NULL);
-    }
-
-    if (!nm_connection_normalize(connection, NULL, NULL, &error)) {
-        _LOGW(LOGD_CORE, "Generated an invalid connection: %s", error->message);
-        nm_clear_pointer(&connection, g_object_unref);
-    }
-
-    return g_steal_pointer(&connection);
-}
diff --git a/src/core/initrd/nmi-ibft-reader.c b/src/core/initrd/nmi-ibft-reader.c
deleted file mode 100644
index 9895cda4..00000000
--- a/src/core/initrd/nmi-ibft-reader.c
+++ /dev/null
@@ -1,463 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2014 - 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-initrd-generator.h"
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <sys/wait.h>
-#include <sys/inotify.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <linux/if_ether.h>
-
-#include "nm-core-internal.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);
-
-        g_object_set(s_ip6,
-                     NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
-                     (int) NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
-                     NULL);
-    }
-
-    family = get_ip_address_family(s_ipaddr, FALSE);
-    if (family == AF_UNSPEC)
-        family = get_ip_address_family(s_gateway, FALSE);
-
-    switch (family) {
-    case AF_INET:
-        s_ip = s_ip4;
-        g_object_set(s_ip6,
-                     NM_SETTING_IP_CONFIG_METHOD,
-                     NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
-                     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 ((nm_streq0(s_origin, "3") && family == AF_INET)
-        || (nm_streq0(s_origin, "4") && family == AF_INET)) {
-        method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
-    } else if (nm_streq0(s_origin, "3") && family == AF_INET6) {
-        method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
-    } else if (nm_streq0(s_origin, "4") && 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_is_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_is_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_is_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,
-                 NM_SETTING_CONNECTION_INTERFACE_NAME,
-                 NULL,
-                 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/core/initrd/tests/meson.build b/src/core/initrd/tests/meson.build
deleted file mode 100644
index e47ff790..00000000
--- a/src/core/initrd/tests/meson.build
+++ /dev/null
@@ -1,24 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-test_units = [
-  'test-dt-reader',
-  'test-ibft-reader',
-  'test-cmdline-reader',
-]
-
-foreach test_unit : test_units
-  exe = executable(
-    test_unit,
-    test_unit + '.c',
-    dependencies: libNetworkManagerTest_dep,
-    c_args: test_c_flags,
-    link_with: libnmi_core,
-  )
-
-  test(
-    'initrd/' + test_unit,
-    test_script,
-    timeout: default_test_timeout,
-    args: test_args + [exe.full_path()],
-  )
-endforeach
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/class/net/eth0/address b/src/core/initrd/tests/sysfs-bad-dns1/class/net/eth0/address
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway
deleted file mode 100644
index bf6cf454..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/gateway
+++ /dev/null
@@ -1 +0,0 @@
-192.168.35.254
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr
deleted file mode 100644
index 944c8d45..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-192.168.32.72
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len
deleted file mode 100644
index 8fdd954d..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-22
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns
deleted file mode 100644
index 2303dbdc..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10000.500.250.1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns
deleted file mode 100644
index 2eb9e1d0..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask
deleted file mode 100644
index 7cb9d0a9..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.252.0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 509a7ab8..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.pjones6
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name b/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name
deleted file mode 100644
index 32c9450a..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns1/firmware/ibft/target0/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/class/net/eth0/address b/src/core/initrd/tests/sysfs-bad-dns2/class/net/eth0/address
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway
deleted file mode 100644
index bf6cf454..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/gateway
+++ /dev/null
@@ -1 +0,0 @@
-192.168.35.254
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr
deleted file mode 100644
index 944c8d45..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-192.168.32.72
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len
deleted file mode 100644
index 8fdd954d..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-22
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns
deleted file mode 100644
index 2e152cb0..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.2
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns
deleted file mode 100644
index 5ed7b719..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-blah.foo.bar.baz
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask
deleted file mode 100644
index 7cb9d0a9..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.252.0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 509a7ab8..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.pjones6
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name b/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name
deleted file mode 100644
index 32c9450a..00000000
--- a/src/core/initrd/tests/sysfs-bad-dns2/firmware/ibft/target0/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/class/net/eth0/address b/src/core/initrd/tests/sysfs-bad-gateway/class/net/eth0/address
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway
deleted file mode 100644
index 1970856e..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/gateway
+++ /dev/null
@@ -1 +0,0 @@
-bb.cc.dd.ee
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr
deleted file mode 100644
index 944c8d45..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-192.168.32.72
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len
deleted file mode 100644
index 8fdd954d..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-22
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns
deleted file mode 100644
index 2e152cb0..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.2
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns
deleted file mode 100644
index 2eb9e1d0..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask
deleted file mode 100644
index 7cb9d0a9..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.252.0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 509a7ab8..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.pjones6
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name b/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name
deleted file mode 100644
index 32c9450a..00000000
--- a/src/core/initrd/tests/sysfs-bad-gateway/firmware/ibft/target0/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address b/src/core/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway
deleted file mode 100644
index bf6cf454..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/gateway
+++ /dev/null
@@ -1 +0,0 @@
-192.168.35.254
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr
deleted file mode 100644
index d526f324..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-aa.bb.cc.dd
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len
deleted file mode 100644
index 8fdd954d..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-22
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns
deleted file mode 100644
index 2e152cb0..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.2
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns
deleted file mode 100644
index 2eb9e1d0..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask
deleted file mode 100644
index 7cb9d0a9..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.252.0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 509a7ab8..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.pjones6
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name b/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name
deleted file mode 100644
index 32c9450a..00000000
--- a/src/core/initrd/tests/sysfs-bad-ipaddr/firmware/ibft/target0/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/class/net/eth0/address b/src/core/initrd/tests/sysfs-dhcp/class/net/eth0/address
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/class/net/eth1/address b/src/core/initrd/tests/sysfs-dhcp/class/net/eth1/address
deleted file mode 100644
index befa7850..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/class/net/eth1/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway
deleted file mode 100644
index 59ea5b82..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/gateway
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.254
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin
deleted file mode 100644
index e440e5c8..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns
deleted file mode 100644
index 2e152cb0..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.2
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns
deleted file mode 100644
index 2eb9e1d0..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway
deleted file mode 100644
index 59ea5b82..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/gateway
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.254
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/index
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac
deleted file mode 100644
index befa7850..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin
deleted file mode 100644
index e440e5c8..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/origin
+++ /dev/null
@@ -1 +0,0 @@
-3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns
deleted file mode 100644
index 2e152cb0..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.2
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns
deleted file mode 100644
index 2eb9e1d0..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/ethernet1/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 509a7ab8..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.pjones6
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name
deleted file mode 100644
index 32c9450a..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target0/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/index
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name b/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name
deleted file mode 100644
index ac3a6a2a..00000000
--- a/src/core/initrd/tests/sysfs-dhcp/firmware/ibft/target1/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.1.2008-11.com.blahblah:iscsi1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpath b/src/core/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpath
deleted file mode 100644
index 6f069ae0..00000000
--- a/src/core/initrd/tests/sysfs-dt-tftp/firmware/devicetree/base/chosen/bootpath
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-request b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-request
deleted file mode 100644
index 034d423e..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-request
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-response b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-response
deleted file mode 100644
index 25982d6f..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootp-response
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpath b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpath
deleted file mode 100644
index db880702..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/bootpath
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip
deleted file mode 100644
index 7bde8641..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/broadcast-ip
+++ /dev/null
@@ -1 +0,0 @@
-ÿÿÿÿ
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip
deleted file mode 100644
index f108ba9b..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-ip
+++ /dev/null
@@ -1,2 +0,0 @@
-
-+
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-name b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-name
deleted file mode 100644
index fe0f76b8..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/client-name
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-name b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-name
deleted file mode 100644
index b34f9f96..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/domain-name
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip
deleted file mode 100644
index f0361835..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/gateway-ip
+++ /dev/null
@@ -1,2 +0,0 @@
-
-+þ
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/name b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/name
deleted file mode 100644
index f3e58052..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/name
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ip b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ip
deleted file mode 100644
index d441cd83..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/netmask-ip
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-path b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-path
deleted file mode 100644
index f76dd238..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/root-path
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip
deleted file mode 100644
index a2a4b2e8..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/server-ip
+++ /dev/null
@@ -1,2 +0,0 @@
-
-&
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-file b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-file
deleted file mode 100644
index c0e0e330..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/tftp-file
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-options b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-options
deleted file mode 100644
index f76dd238..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/chosen/vendor-options
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_type b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_type
deleted file mode 100644
index df3c9d9f..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/device_type
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address
deleted file mode 100644
index c983e752..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/local-mac-address
+++ /dev/null
@@ -1 +0,0 @@
-¬>åØ
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address
deleted file mode 100644
index c983e752..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/mac-address
+++ /dev/null
@@ -1 +0,0 @@
-¬>åØ
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/name b/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/name
deleted file mode 100644
index 88eaddfe..00000000
--- a/src/core/initrd/tests/sysfs-dt/firmware/devicetree/base/ethernet/name
+++ /dev/null
Binary files differdiff --git a/src/core/initrd/tests/sysfs-static/class/net/eth0/address b/src/core/initrd/tests/sysfs-static/class/net/eth0/address
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-static/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/class/net/eth1/address b/src/core/initrd/tests/sysfs-static/class/net/eth1/address
deleted file mode 100644
index befa7850..00000000
--- a/src/core/initrd/tests/sysfs-static/class/net/eth1/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway
deleted file mode 100644
index bf6cf454..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/gateway
+++ /dev/null
@@ -1 +0,0 @@
-192.168.35.254
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr
deleted file mode 100644
index 944c8d45..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-192.168.32.72
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len
deleted file mode 100644
index 8fdd954d..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-22
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns
deleted file mode 100644
index 2e152cb0..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.2
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns
deleted file mode 100644
index 2eb9e1d0..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask
deleted file mode 100644
index 7cb9d0a9..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.252.0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway
deleted file mode 100644
index 59ea5b82..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/gateway
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.254
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/index
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac
deleted file mode 100644
index befa7850..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin
deleted file mode 100644
index e440e5c8..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/origin
+++ /dev/null
@@ -1 +0,0 @@
-3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns
deleted file mode 100644
index 2e152cb0..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.2
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns
deleted file mode 100644
index 2eb9e1d0..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-10.16.255.3
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan b/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/ethernet1/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 509a7ab8..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.pjones6
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/target-name b/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/target-name
deleted file mode 100644
index 32c9450a..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target0/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.0.2008-11.com.blahblah:iscsi0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/index b/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/index
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/index
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr b/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr
deleted file mode 100644
index 7450fdf9..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-10.16.52.16
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/lun b/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/lun
deleted file mode 100644
index c43d0930..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/lun
+++ /dev/null
@@ -1 +0,0 @@
-00000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/port b/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/target-name b/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/target-name
deleted file mode 100644
index ac3a6a2a..00000000
--- a/src/core/initrd/tests/sysfs-static/firmware/ibft/target1/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.1.2008-11.com.blahblah:iscsi1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/class/net/eth0/address b/src/core/initrd/tests/sysfs-vlan/class/net/eth0/address
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-vlan/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr
deleted file mode 100644
index 4923230c..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-192.168.6.200
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac
deleted file mode 100644
index 20659db7..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:33:21:98:b9:f0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin
deleted file mode 100644
index 56a6051c..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len
deleted file mode 100644
index cabf43b5..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-24
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask
deleted file mode 100644
index 5a279b41..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.255.0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index d800886d..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-123
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 8e9cd2c4..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/index
deleted file mode 100644
index c2270834..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr
deleted file mode 100644
index a32ce01b..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-192.168.6.32
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun
deleted file mode 100644
index f309ca6a..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-01000000
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/port
deleted file mode 100644
index 58f0d919..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name b/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name
deleted file mode 100644
index f0e61157..00000000
--- a/src/core/initrd/tests/sysfs-vlan/firmware/ibft/target0/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.2003-01.org.x:disk1
\ No newline at end of file
diff --git a/src/core/initrd/tests/sysfs/class/net/eth0/address b/src/core/initrd/tests/sysfs/class/net/eth0/address
deleted file mode 100644
index 1f2f610e..00000000
--- a/src/core/initrd/tests/sysfs/class/net/eth0/address
+++ /dev/null
@@ -1 +0,0 @@
-00:53:00:AB:00:01
diff --git a/src/core/initrd/tests/sysfs/class/net/eth2/address b/src/core/initrd/tests/sysfs/class/net/eth2/address
deleted file mode 100644
index 6ec1078a..00000000
--- a/src/core/initrd/tests/sysfs/class/net/eth2/address
+++ /dev/null
@@ -1 +0,0 @@
-00:53:06:66:ab:01
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id b/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id
deleted file mode 100644
index 590d9cd2..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_id
+++ /dev/null
@@ -1 +0,0 @@
-NMTst
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id b/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id
deleted file mode 100644
index 35d4d09c..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/oem_table_id
+++ /dev/null
@@ -1 +0,0 @@
-Whatevs
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/signature b/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/signature
deleted file mode 100644
index e3a5d7ef..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/acpi_header/signature
+++ /dev/null
@@ -1 +0,0 @@
-IBFT
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp
deleted file mode 100644
index 98a0d797..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/dhcp
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:0002
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/flags b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/flags
deleted file mode 100644
index 00750edc..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/flags
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway
deleted file mode 100644
index 7f378f8f..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/gateway
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:0001
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname
deleted file mode 100644
index 3ed5163c..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/hostname
+++ /dev/null
@@ -1 +0,0 @@
-host0.example.com
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/index b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/index
deleted file mode 100644
index 573541ac..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr
deleted file mode 100644
index 98a0d797..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:0002
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/mac b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/mac
deleted file mode 100644
index f2441319..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:53:00:ab:00:01
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/origin b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/origin
deleted file mode 100644
index b8626c4c..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/origin
+++ /dev/null
@@ -1 +0,0 @@
-4
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len
deleted file mode 100644
index 900731ff..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-64
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns
deleted file mode 100644
index e7511d5a..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:0053
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns
deleted file mode 100644
index 1f6c5c95..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:5353
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask
deleted file mode 100644
index 5df93454..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.255.255
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan
deleted file mode 100644
index 7cc86ad1..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet0/vlan
+++ /dev/null
@@ -1 +0,0 @@
-666
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp
deleted file mode 100644
index 61ef5585..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/dhcp
+++ /dev/null
@@ -1 +0,0 @@
-192.0.2.2
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/flags b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/flags
deleted file mode 100644
index 00750edc..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/flags
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway
deleted file mode 100644
index 2096f194..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/gateway
+++ /dev/null
@@ -1 +0,0 @@
-192.0.2.1
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname
deleted file mode 100644
index 5dc539d5..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/hostname
+++ /dev/null
@@ -1 +0,0 @@
-host2.example.com
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/index b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/index
deleted file mode 100644
index 0cfbf088..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/index
+++ /dev/null
@@ -1 +0,0 @@
-2
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr
deleted file mode 100644
index 61ef5585..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-192.0.2.2
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/mac b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/mac
deleted file mode 100644
index 2fc3d525..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/mac
+++ /dev/null
@@ -1 +0,0 @@
-00:53:06:66:AB:01
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/origin b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/origin
deleted file mode 100644
index 00750edc..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/origin
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len
deleted file mode 100644
index a45fd52c..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/prefix-len
+++ /dev/null
@@ -1 +0,0 @@
-24
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns
deleted file mode 100644
index 6fa8c742..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/primary-dns
+++ /dev/null
@@ -1 +0,0 @@
-192.0.2.53
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns
deleted file mode 100644
index a7269ff5..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/secondary-dns
+++ /dev/null
@@ -1 +0,0 @@
-192.0.2.54
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask
deleted file mode 100644
index d30f9e9f..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/subnet-mask
+++ /dev/null
@@ -1 +0,0 @@
-255.255.255.0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan b/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan
deleted file mode 100644
index 573541ac..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/ethernet2/vlan
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/flags b/src/core/initrd/tests/sysfs/firmware/ibft/initiator/flags
deleted file mode 100644
index 00750edc..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/flags
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/index b/src/core/initrd/tests/sysfs/firmware/ibft/initiator/index
deleted file mode 100644
index 573541ac..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/index
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name b/src/core/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name
deleted file mode 100644
index 337b5a0f..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/initiator-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.1994-05.com.example.initiator:48b055856417
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/isns-server b/src/core/initrd/tests/sysfs/firmware/ibft/initiator/isns-server
deleted file mode 100644
index 4aff9398..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/isns-server
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:3205
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server b/src/core/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server
deleted file mode 100644
index a26fb649..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/pri-radius-server
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:1812
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server b/src/core/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server
deleted file mode 100644
index 908be27f..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/sec-radius-server
+++ /dev/null
@@ -1 +0,0 @@
-192.0.2.181
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/slp-server b/src/core/initrd/tests/sysfs/firmware/ibft/initiator/slp-server
deleted file mode 100644
index fca9e24b..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/initiator/slp-server
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:0427
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target0/chap-type b/src/core/initrd/tests/sysfs/firmware/ibft/target0/chap-type
deleted file mode 100644
index 573541ac..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target0/chap-type
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target0/flags b/src/core/initrd/tests/sysfs/firmware/ibft/target0/flags
deleted file mode 100644
index 00750edc..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target0/flags
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target0/index b/src/core/initrd/tests/sysfs/firmware/ibft/target0/index
deleted file mode 100644
index 573541ac..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target0/index
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target0/ip-addr b/src/core/initrd/tests/sysfs/firmware/ibft/target0/ip-addr
deleted file mode 100644
index 994b6e9c..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target0/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0000:3260
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target0/lun b/src/core/initrd/tests/sysfs/firmware/ibft/target0/lun
deleted file mode 100644
index 7c295bd4..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target0/lun
+++ /dev/null
@@ -1 +0,0 @@
-01000000
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc b/src/core/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc
deleted file mode 100644
index 573541ac..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target0/nic-assoc
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target0/port b/src/core/initrd/tests/sysfs/firmware/ibft/target0/port
deleted file mode 100644
index 53f0295f..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target0/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/chap-type b/src/core/initrd/tests/sysfs/firmware/ibft/target2/chap-type
deleted file mode 100644
index 573541ac..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/chap-type
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/flags b/src/core/initrd/tests/sysfs/firmware/ibft/target2/flags
deleted file mode 100644
index 00750edc..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/flags
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/index b/src/core/initrd/tests/sysfs/firmware/ibft/target2/index
deleted file mode 100644
index 0cfbf088..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/index
+++ /dev/null
@@ -1 +0,0 @@
-2
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/ip-addr b/src/core/initrd/tests/sysfs/firmware/ibft/target2/ip-addr
deleted file mode 100644
index a210477e..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/ip-addr
+++ /dev/null
@@ -1 +0,0 @@
-2001:0db8:0000:0000:0000:0000:0002:3260
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/lun b/src/core/initrd/tests/sysfs/firmware/ibft/target2/lun
deleted file mode 100644
index 7c295bd4..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/lun
+++ /dev/null
@@ -1 +0,0 @@
-01000000
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc b/src/core/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc
deleted file mode 100644
index 0cfbf088..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/nic-assoc
+++ /dev/null
@@ -1 +0,0 @@
-2
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/port b/src/core/initrd/tests/sysfs/firmware/ibft/target2/port
deleted file mode 100644
index 53f0295f..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/port
+++ /dev/null
@@ -1 +0,0 @@
-3260
diff --git a/src/core/initrd/tests/sysfs/firmware/ibft/target2/target-name b/src/core/initrd/tests/sysfs/firmware/ibft/target2/target-name
deleted file mode 100644
index f4094320..00000000
--- a/src/core/initrd/tests/sysfs/firmware/ibft/target2/target-name
+++ /dev/null
@@ -1 +0,0 @@
-iqn.1994-05.com.example.target:48b055851337
diff --git a/src/core/initrd/tests/test-cmdline-reader.c b/src/core/initrd/tests/test-cmdline-reader.c
deleted file mode 100644
index 33fb22d3..00000000
--- a/src/core/initrd/tests/test-cmdline-reader.c
+++ /dev/null
@@ -1,2182 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <unistd.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"
-
-#define TEST_INITRD_DIR NM_BUILD_SRCDIR "/src/core/initrd/tests"
-
-/*****************************************************************************/
-
-#define _parse(ARGV, out_hostname, out_carrier_timeout_sec)                            \
-    ({                                                                                 \
-        const char *const *const _ARGV                    = (ARGV);                    \
-        char **const             _out_hostname            = (out_hostname);            \
-        gint64 *const            _out_carrier_timeout_sec = (out_carrier_timeout_sec); \
-        GHashTable *             _connections;                                         \
-                                                                                       \
-        _connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs",              \
-                                                _ARGV,                                 \
-                                                _out_hostname,                         \
-                                                _out_carrier_timeout_sec);             \
-                                                                                       \
-        g_assert(_connections);                                                        \
-                                                                                       \
-        _connections;                                                                  \
-    })
-
-#define _parse_cons(ARGV)                                                                    \
-    ({                                                                                       \
-        GHashTable *  _con_connections;                                                      \
-        gs_free char *_con_hostname            = NULL;                                       \
-        gint64        _con_carrier_timeout_sec = 0;                                          \
-                                                                                             \
-        _con_connections = _parse((ARGV),                                                    \
-                                  nmtst_get_rand_bool() ? &_con_hostname : NULL,             \
-                                  nmtst_get_rand_bool() ? &_con_carrier_timeout_sec : NULL); \
-        g_assert_cmpstr(_con_hostname, ==, NULL);                                            \
-        g_assert_cmpint(_con_carrier_timeout_sec, ==, 0);                                    \
-                                                                                             \
-        _con_connections;                                                                    \
-    })
-
-#define _parse_con(ARGV, connection_name)                                        \
-    ({                                                                           \
-        gs_unref_hashtable GHashTable *_1_connections = NULL;                    \
-        NMConnection *                 _1_connection;                            \
-        const char *const              _1_connection_name = (connection_name);   \
-                                                                                 \
-        g_assert(_1_connection_name);                                            \
-                                                                                 \
-        _1_connections = _parse_cons((ARGV));                                    \
-                                                                                 \
-        g_assert_cmpint(g_hash_table_size(_1_connections), ==, 1);               \
-                                                                                 \
-        _1_connection = g_hash_table_lookup(_1_connections, _1_connection_name); \
-        g_assert(NM_IS_CONNECTION(_1_connection));                               \
-                                                                                 \
-        nmtst_assert_connection_verifies_without_normalization(_1_connection);   \
-                                                                                 \
-        NM_CONNECTION(g_object_ref(_1_connection));                              \
-    })
-
-/*****************************************************************************/
-
-static void
-test_auto(void)
-{
-    const char *const *ARGV                  = NM_MAKE_STRV("ip=auto");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingConnection *         s_con;
-    NMSettingWired *              s_wired;
-    NMSettingIPConfig *           s_ip4;
-    NMSettingIPConfig *           s_ip6;
-
-    connection = _parse_con(ARGV, "default_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_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-
-    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_dhcp_with_hostname(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("ip=::::host1::dhcp");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingWired *               s_wired;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    gs_free char *                 hostname            = NULL;
-    gint64                         carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 1);
-    g_assert_cmpstr(hostname, ==, "host1");
-    g_assert_cmpint(carrier_timeout_sec, ==, 0);
-
-    connection = g_hash_table_lookup(connections, "default_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_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-
-    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);
-
-    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);
-}
-
-static void
-test_dhcp_with_mtu(void)
-{
-    const char *const *ARGV0  = NM_MAKE_STRV("ip=:dhcp:1499");
-    const char *const *ARGV1  = NM_MAKE_STRV("ip=::::::dhcp:1499");
-    const char *const *ARGV[] = {ARGV0, ARGV1};
-    guint              i;
-
-    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
-        gs_unref_object NMConnection *connection = NULL;
-        NMSettingConnection *         s_con;
-        NMSettingWired *              s_wired;
-        NMSettingIPConfig *           s_ip4;
-        NMSettingIPConfig *           s_ip6;
-
-        connection = _parse_con(ARGV[i], "default_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_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                        ==,
-                        NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
-        g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-
-        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), ==, 1499);
-
-        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_AUTO);
-    }
-}
-
-static void
-test_dhcp_timeout(void)
-{
-    struct {
-        const char *const *cmdline;
-        int                timeout;
-    } data[] = {
-        {NM_MAKE_STRV("ip=dhcp"), 90},
-        {NM_MAKE_STRV("ip=dhcp", "rd.net.timeout.dhcp=0"), 90},
-        {NM_MAKE_STRV("ip=dhcp", "rd.net.timeout.dhcp=foobar"), 90},
-        {NM_MAKE_STRV("ip=dhcp", "rd.net.timeout.dhcp=42"), 42},
-        {NM_MAKE_STRV("ip=dhcp", "rd.net.dhcp.retry=2"), 180},
-        {NM_MAKE_STRV("ip=dhcp", "rd.net.dhcp.retry=3", "rd.net.timeout.dhcp=40"), 120},
-        {NM_MAKE_STRV("ip=dhcp", "rd.net.timeout.dhcp=infinity"), G_MAXINT32},
-        {NM_MAKE_STRV("ip=dhcp", "rd.net.timeout.dhcp=infinity", "rd.net.dhcp.retry=100"),
-         G_MAXINT32},
-    };
-    guint i;
-
-    for (i = 0; i < G_N_ELEMENTS(data); i++) {
-        gs_unref_object NMConnection *connection = NULL;
-        NMSettingConnection *         s_con;
-        NMSettingIPConfig *           s_ip4;
-        NMSettingIPConfig *           s_ip6;
-
-        connection = _parse_con(data[i].cmdline, "default_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_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                        ==,
-                        NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
-        g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-        g_assert_cmpint(nm_setting_connection_get_autoconnect_retries(s_con), ==, 1);
-        g_assert(nm_setting_connection_get_autoconnect(s_con));
-
-        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_cmpint(nm_setting_ip_config_get_dhcp_timeout(s_ip4), ==, data[i].timeout);
-
-        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_dhcp_timeout(s_ip6), ==, data[i].timeout);
-    }
-}
-
-static void
-test_if_auto_with_mtu(void)
-{
-    const char *const *ARGV                  = NM_MAKE_STRV("ip=eth0:auto:1666");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingConnection *         s_con;
-    NMSettingWired *              s_wired;
-    NMSettingIPConfig *           s_ip4;
-    NMSettingIPConfig *           s_ip6;
-
-    connection = _parse_con(ARGV, "eth0");
-
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0");
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpint(nm_setting_connection_get_autoconnect_retries(s_con), ==, 1);
-
-    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));
-    g_assert_cmpint(nm_setting_ip_config_get_dhcp_timeout(s_ip4), ==, 90);
-
-    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)
-{
-    const char *const *ARGV                  = NM_MAKE_STRV("ip=eth1:dhcp6");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingIPConfig *           s_ip4;
-    NMSettingIPConfig *           s_ip6;
-
-    connection = _parse_con(ARGV, "eth1");
-
-    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_AUTO);
-    g_assert(!nm_setting_ip_config_get_ignore_auto_dns(s_ip6));
-}
-
-static void
-test_if_auto_with_mtu_and_mac(void)
-{
-    const char *const *ARGV                  = NM_MAKE_STRV("ip=eth2:auto6:2048:00:53:ef:12:34:56");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingWired *              s_wired;
-    NMSettingIPConfig *           s_ip4;
-    NMSettingIPConfig *           s_ip6;
-
-    connection = _parse_con(ARGV, "eth2");
-
-    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;
-    const char *const *            ARGV = NM_MAKE_STRV("ip=192.0.2.2::192.0.2.1:255.255.255.0:"
-                                           "hostname0.example.com:eth3:none:192.0.2.53",
-                                           "ip=203.0.113.2::203.0.113.1:26:"
-                                           "hostname1.example.com:eth4");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    NMIPAddress *                  ip_addr;
-    gs_free char *                 hostname            = NULL;
-    gint64                         carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-    g_assert_cmpstr(hostname, ==, "hostname1.example.com");
-    g_assert_cmpint(carrier_timeout_sec, ==, 0);
-
-    connection = g_hash_table_lookup(connections, "eth3");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth3");
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-
-    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");
-
-    s_ip6 = nm_connection_get_setting_ip6_config(connection);
-    g_assert(s_ip6);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
-                    ==,
-                    NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
-    g_assert(nm_setting_ip_config_get_may_fail(s_ip6));
-
-    connection = g_hash_table_lookup(connections, "eth4");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth4");
-
-    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), ==, 0);
-    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), ==, "203.0.113.2");
-    g_assert_cmpint(nm_ip_address_get_prefix(ip_addr), ==, 26);
-    g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "203.0.113.1");
-    g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip4), ==, "hostname1.example.com");
-
-    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_may_fail(s_ip6));
-}
-
-static void
-test_if_ip6_manual(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV = NM_MAKE_STRV("ip=[2001:0db8::02]/64::[2001:0db8::01]::"
-                                           "hostname0.example.com:eth4::[2001:0db8::53]");
-    NMConnection *                 connection;
-    NMSettingIPConfig *            s_ip6;
-    NMIPAddress *                  ip_addr;
-    gs_free char *                 hostname            = NULL;
-    gint64                         carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 1);
-    g_assert_cmpstr(hostname, ==, "hostname0.example.com");
-    g_assert_cmpint(carrier_timeout_sec, ==, 0);
-
-    connection = g_hash_table_lookup(connections, "eth4");
-    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_if_off(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("ip=off",
-                                           "ip=ens3:off",
-                                           "ip=10.0.0.8:::::ens4:off",
-                                           "ip=[2001:DB8::8]:::::ens5:off");
-    NMConnection *                 connection;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    struct {
-        const char name[32];
-        const char ipv4_method[32];
-        const char ipv6_method[32];
-
-    } conn_expected[] = {
-        {"default_connection",
-         NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
-         NM_SETTING_IP6_CONFIG_METHOD_DISABLED},
-        {"ens3", NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NM_SETTING_IP6_CONFIG_METHOD_DISABLED},
-        {"ens4", NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NM_SETTING_IP6_CONFIG_METHOD_DISABLED},
-        {"ens5", NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NM_SETTING_IP6_CONFIG_METHOD_MANUAL},
-    };
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, G_N_ELEMENTS(conn_expected));
-
-    for (int i = 0; i < G_N_ELEMENTS(conn_expected); ++i) {
-        connection = g_hash_table_lookup(connections, conn_expected[i].name);
-        nmtst_assert_connection_verifies_without_normalization(connection);
-
-        s_ip4 = nm_connection_get_setting_ip4_config(connection);
-        g_assert(s_ip4);
-        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, conn_expected[i].ipv4_method);
-
-        s_ip6 = nm_connection_get_setting_ip6_config(connection);
-        g_assert(s_ip6);
-        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, conn_expected[i].ipv6_method);
-    }
-}
-
-static void
-test_if_mac_ifname(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV = NM_MAKE_STRV("ip=[2001:0db8::42]/64::[2001:0db8::01]::"
-                                           "hostname0:00-11-22-33-44-55::[2001:0db8::53]");
-    NMConnection *                 connection;
-    NMSettingIPConfig *            s_ip6;
-    NMSettingWired *               s_wired;
-    NMIPAddress *                  ip_addr;
-    gs_free char *                 hostname            = NULL;
-    gint64                         carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 1);
-    g_assert_cmpstr(hostname, ==, "hostname0");
-    g_assert_cmpint(carrier_timeout_sec, ==, 0);
-
-    connection = g_hash_table_lookup(connections, "00:11:22:33:44:55");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "00:11:22:33:44:55");
-    g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, NULL);
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(s_wired);
-    g_assert_cmpstr(nm_setting_wired_get_mac_address(s_wired), ==, "00:11:22:33:44:55");
-
-    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::42");
-    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");
-}
-
-static void
-test_multiple_merge(void)
-{
-    const char *const *ARGV =
-        NM_MAKE_STRV("ip=192.0.2.2/16:::::eth0", "ip=[2001:db8::2]:::56::eth0");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingConnection *         s_con;
-    NMSettingWired *              s_wired;
-    NMSettingIPConfig *           s_ip4;
-    NMSettingIPConfig *           s_ip6;
-    NMIPAddress *                 ip_addr;
-
-    connection = _parse_con(ARGV, "eth0");
-
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0");
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(s_wired);
-
-    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");
-    g_assert_cmpint(nm_ip_address_get_prefix(ip_addr), ==, 16);
-
-    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");
-    g_assert_cmpint(nm_ip_address_get_prefix(ip_addr), ==, 56);
-}
-
-static void
-test_multiple_bootdev(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-
-    const char *const *ARGV = NM_MAKE_STRV("nameserver=1.2.3.4",
-                                           "ip=eth3:auto6",
-                                           "ip=eth4:dhcp",
-                                           "ip=eth5:link6",
-                                           "bootdev=eth4");
-
-    NMConnection *       connection;
-    NMSettingConnection *s_con;
-    NMSettingIPConfig *  s_ip4;
-    NMSettingIPConfig *  s_ip6;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
-
-    connection = g_hash_table_lookup(connections, "eth3");
-    g_assert(connection);
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-    s_ip6 = nm_connection_get_setting_ip6_config(connection);
-    g_assert(s_ip6);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
-
-    connection = g_hash_table_lookup(connections, "eth4");
-    g_assert(connection);
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con),
-                    ==,
-                    NMI_WAIT_DEVICE_TIMEOUT_MS);
-    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_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 1);
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "1.2.3.4");
-
-    connection = g_hash_table_lookup(connections, "eth5");
-    g_assert(connection);
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-    s_ip6 = nm_connection_get_setting_ip6_config(connection);
-    g_assert(s_ip6);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
-                    ==,
-                    NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL);
-}
-
-static void
-test_bootdev(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("vlan=vlan2:ens5", "bootdev=ens3");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
-
-    connection = g_hash_table_lookup(connections, "ens3");
-    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), ==, "ens3");
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "ens3");
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con),
-                    ==,
-                    NMI_WAIT_DEVICE_TIMEOUT_MS);
-
-    connection = g_hash_table_lookup(connections, "vlan2");
-    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);
-    g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "vlan2");
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "vlan2");
-
-    connection = g_hash_table_lookup(connections, "ens5");
-    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), ==, "ens5");
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "ens5");
-}
-
-static void
-test_some_more(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("bootdev=eth1",
-                                           "hail",
-                                           "nameserver=[2001:DB8:3::53]",
-                                           "satan",
-                                           "nameserver=192.0.2.53",
-                                           "worship",
-                                           "doom",
-                                           "rd.peerdns=0",
-                                           "rd.route=[2001:DB8:3::/48]:[2001:DB8:2::1]:ens10");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingWired *               s_wired;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    NMIPRoute *                    ip_route;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-
-    connection = g_hash_table_lookup(connections, "eth1");
-    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), ==, "eth1");
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "eth1");
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(s_wired);
-
-    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");
-    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");
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-
-    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), ==, 1);
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 0), ==, "2001:db8:3::53");
-    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_bond(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("rd.route=192.0.2.53::bong0",
-                                           "bond=bong0:eth0,eth1:mode=balance-rr",
-                                           "nameserver=203.0.113.53");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    NMSettingBond *                s_bond;
-    NMIPRoute *                    ip_route;
-    const char *                   master_uuid;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
-
-    connection = g_hash_table_lookup(connections, "bong0");
-    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), ==, 1);
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "203.0.113.53");
-    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");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-
-    connection = g_hash_table_lookup(connections, "eth1");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-}
-
-static void
-test_bond_ip(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV =
-        NM_MAKE_STRV("bond=bond0:eth0,eth1",
-                     "ip=192.168.1.1::192.168.1.254:24::bond0:none:1480:01:02:03:04:05:06",
-                     "nameserver=4.8.15.16");
-    NMConnection *       connection;
-    NMSettingConnection *s_con;
-    NMSettingIPConfig *  s_ip4;
-    NMSettingIPConfig *  s_ip6;
-    NMSettingWired *     s_wired;
-    NMSettingBond *      s_bond;
-    NMIPAddress *        ip_addr;
-    const char *         master_uuid;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
-
-    connection = g_hash_table_lookup(connections, "bond0");
-    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_wired = nm_connection_get_setting_wired(connection);
-    g_assert(s_wired);
-    g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 1480);
-    g_assert_cmpstr(nm_setting_wired_get_cloned_mac_address(s_wired), ==, "01:02:03:04:05:06");
-
-    s_ip4 = nm_connection_get_setting_ip4_config(connection);
-    g_assert(s_ip4);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
-                    ==,
-                    NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
-    g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1);
-    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.168.1.1");
-    g_assert_cmpint(nm_ip_address_get_prefix(ip_addr), ==, 24);
-    g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "192.168.1.254");
-    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), ==, "4.8.15.16");
-    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_DISABLED);
-    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");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-
-    connection = g_hash_table_lookup(connections, "eth1");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-}
-
-static void
-test_bond_default(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("bond");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    NMSettingBond *                s_bond;
-    const char *                   master_uuid;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-
-    connection = g_hash_table_lookup(connections, "bond0");
-
-    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");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-}
-
-static void
-test_bridge(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("bridge=bridge0:eth0,eth1",
-                                           "rd.route=192.0.2.53::bridge0",
-                                           "rd.net.timeout.dhcp=10");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    NMSettingBridge *              s_bridge;
-    NMIPRoute *                    ip_route;
-    const char *                   master_uuid;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
-
-    connection = g_hash_table_lookup(connections, "bridge0");
-    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);
-    g_assert_cmpint(nm_setting_ip_config_get_dhcp_timeout(s_ip4), ==, 10);
-    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);
-    g_assert_cmpint(nm_setting_ip_config_get_dhcp_timeout(s_ip6), ==, 10);
-
-    s_bridge = nm_connection_get_setting_bridge(connection);
-    g_assert(s_bridge);
-    g_assert_cmpint(nm_setting_bridge_get_stp(s_bridge), ==, FALSE);
-
-    connection = g_hash_table_lookup(connections, "eth0");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-
-    connection = g_hash_table_lookup(connections, "eth1");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-}
-
-static void
-test_bridge_default(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("bridge");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    NMSettingBridge *              s_bridge;
-    const char *                   master_uuid;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-
-    connection = g_hash_table_lookup(connections, "br0");
-
-    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), ==, "br0");
-    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");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-}
-
-static void
-test_bridge_ip(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV =
-        NM_MAKE_STRV("ip=bridge123:auto:1280:00:11:22:33:CA:fe",
-                     "bridge=bridge123:eth0,eth1,eth2,eth3,eth4,eth5,eth6,eth7,eth8,eth9");
-    NMConnection *       connection;
-    NMSettingConnection *s_con;
-    NMSettingIPConfig *  s_ip4;
-    NMSettingIPConfig *  s_ip6;
-    NMSettingWired *     s_wired;
-    NMSettingBridge *    s_bridge;
-    const char *         master_uuid;
-    guint                i;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 11);
-
-    connection = g_hash_table_lookup(connections, "bridge123");
-    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), ==, "bridge123");
-    master_uuid = nm_connection_get_uuid(connection);
-    g_assert(master_uuid);
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(s_wired);
-    g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 1280);
-    g_assert_cmpstr(nm_setting_wired_get_cloned_mac_address(s_wired), ==, "00:11:22:33:CA:FE");
-
-    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_AUTO);
-
-    s_bridge = nm_connection_get_setting_bridge(connection);
-    g_assert(s_bridge);
-
-    for (i = 0; i < 10; i++) {
-        char ifname[16];
-
-        nm_sprintf_buf(ifname, "eth%u", i);
-
-        connection = g_hash_table_lookup(connections, ifname);
-        nmtst_assert_connection_verifies_without_normalization(connection);
-        g_assert_cmpstr(nm_connection_get_id(connection), ==, ifname);
-
-        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), ==, ifname);
-        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);
-        g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                        ==,
-                        NM_CONNECTION_MULTI_CONNECT_SINGLE);
-    }
-}
-
-static void
-test_team(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV = NM_MAKE_STRV("team=team0:eth0,eth1", "ip=team0:dhcp6");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-    NMSettingIPConfig *            s_ip4;
-    NMSettingIPConfig *            s_ip6;
-    NMSettingTeam *                s_team;
-    const char *                   master_uuid;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
-
-    connection = g_hash_table_lookup(connections, "team0");
-    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_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_team = nm_connection_get_setting_team(connection);
-    g_assert(s_team);
-
-    connection = g_hash_table_lookup(connections, "eth0");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-
-    connection = g_hash_table_lookup(connections, "eth1");
-    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);
-    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
-                    ==,
-                    NM_CONNECTION_MULTI_CONNECT_SINGLE);
-}
-
-static void
-test_vlan(void)
-{
-    const char *const *ARGV0  = NM_MAKE_STRV("ip=eth0.100:dhcp", "vlan=eth0.100:eth0");
-    const char *const *ARGV1  = NM_MAKE_STRV("vlan=eth0.100:eth0", "ip=eth0.100:dhcp");
-    const char *const *ARGV[] = {ARGV0, ARGV1};
-    guint              i;
-
-    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
-        gs_unref_hashtable GHashTable *connections = NULL;
-        NMConnection *                 connection;
-        NMSettingIPConfig *            s_ip4;
-        NMSettingIPConfig *            s_ip6;
-        NMSettingVlan *                s_vlan;
-
-        connections = _parse_cons(ARGV[i]);
-        g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-
-        /* VLAN eth0.100 */
-        connection = g_hash_table_lookup(connections, "eth0.100");
-        nmtst_assert_connection_verifies_without_normalization(connection);
-        g_assert_cmpstr(nm_connection_get_connection_type(connection),
-                        ==,
-                        NM_SETTING_VLAN_SETTING_NAME);
-        g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0.100");
-
-        s_vlan = nm_connection_get_setting_vlan(connection);
-        g_assert(s_vlan);
-        g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0");
-        g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 100);
-
-        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_AUTO);
-
-        /* Ethernet eth0 */
-        connection = g_hash_table_lookup(connections, "eth0");
-        nmtst_assert_connection_verifies_without_normalization(connection);
-        g_assert_cmpstr(nm_connection_get_connection_type(connection),
-                        ==,
-                        NM_SETTING_WIRED_SETTING_NAME);
-        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_DISABLED);
-
-        s_ip6 = nm_connection_get_setting_ip6_config(connection);
-        g_assert(s_ip6);
-        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
-                        ==,
-                        NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
-    }
-}
-
-static void
-test_vlan_with_dhcp_on_parent(void)
-{
-    const char *const *ARGV0  = NM_MAKE_STRV("vlan=eth0.100:eth0", "ip=eth0:dhcp");
-    const char *const *ARGV1  = NM_MAKE_STRV("ip=eth0:dhcp", "vlan=eth0.100:eth0");
-    const char *const *ARGV[] = {ARGV0, ARGV1};
-    guint              i;
-
-    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
-        gs_unref_hashtable GHashTable *connections = NULL;
-        NMConnection *                 connection;
-        NMSettingIPConfig *            s_ip4;
-        NMSettingIPConfig *            s_ip6;
-        NMSettingVlan *                s_vlan;
-
-        connections = _parse_cons(ARGV[i]);
-        g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-
-        /* VLAN eth0.100 */
-        connection = g_hash_table_lookup(connections, "eth0.100");
-        nmtst_assert_connection_verifies_without_normalization(connection);
-        g_assert_cmpstr(nm_connection_get_connection_type(connection),
-                        ==,
-                        NM_SETTING_VLAN_SETTING_NAME);
-        g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0.100");
-
-        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_AUTO);
-
-        s_vlan = nm_connection_get_setting_vlan(connection);
-        g_assert(s_vlan);
-        g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0");
-        g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 100);
-
-        /* Ethernet eth0 */
-        connection = g_hash_table_lookup(connections, "eth0");
-        nmtst_assert_connection_verifies_without_normalization(connection);
-        g_assert_cmpstr(nm_connection_get_connection_type(connection),
-                        ==,
-                        NM_SETTING_WIRED_SETTING_NAME);
-        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_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_AUTO);
-    }
-}
-
-static void
-test_vlan_over_bond(void)
-{
-    const char *const *ARGV0  = NM_MAKE_STRV("ip=1.2.3.4:::24::vlan1:none",
-                                            "bond=bond2:ens3,ens4:mode=active-backup",
-                                            "vlan=vlan1:bond2");
-    const char *const *ARGV1  = NM_MAKE_STRV("vlan=vlan1:bond2",
-                                            "ip=1.2.3.4:::24::vlan1:none",
-                                            "bond=bond2:ens3,ens4:mode=active-backup");
-    const char *const *ARGV2  = NM_MAKE_STRV("bond=bond2:ens3,ens4:mode=active-backup",
-                                            "ip=1.2.3.4:::24::vlan1:none",
-                                            "vlan=vlan1:bond2");
-    const char *const *ARGV[] = {ARGV0, ARGV1, ARGV2};
-    guint              i;
-
-    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
-        gs_unref_hashtable GHashTable *connections = NULL;
-        NMConnection *                 connection;
-        NMSettingIPConfig *            s_ip4;
-        NMSettingIPConfig *            s_ip6;
-        NMSettingVlan *                s_vlan;
-
-        connections = _parse_cons(ARGV[i]);
-        g_assert_cmpint(g_hash_table_size(connections), ==, 4);
-
-        /* VLAN vlan1 */
-        connection = g_hash_table_lookup(connections, "vlan1");
-        nmtst_assert_connection_verifies_without_normalization(connection);
-        g_assert_cmpstr(nm_connection_get_connection_type(connection),
-                        ==,
-                        NM_SETTING_VLAN_SETTING_NAME);
-        g_assert_cmpstr(nm_connection_get_id(connection), ==, "vlan1");
-
-        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);
-
-        s_ip6 = nm_connection_get_setting_ip6_config(connection);
-        g_assert(s_ip6);
-        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
-                        ==,
-                        NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
-
-        s_vlan = nm_connection_get_setting_vlan(connection);
-        g_assert(s_vlan);
-        g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "bond2");
-        g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 1);
-
-        /* Bond bond2 */
-        connection = g_hash_table_lookup(connections, "bond2");
-        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), ==, "bond2");
-
-        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);
-
-        s_ip6 = nm_connection_get_setting_ip6_config(connection);
-        g_assert(s_ip6);
-        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
-                        ==,
-                        NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
-
-        /* Ethernet ens3 and ens4 */
-        connection = g_hash_table_lookup(connections, "ens3");
-        g_assert(connection);
-        connection = g_hash_table_lookup(connections, "ens4");
-        g_assert(connection);
-    }
-}
-
-static void
-test_ibft_ip_dev(void)
-{
-    const char *const *  ARGV = NM_MAKE_STRV("ip=eth0:ibft");
-    NMSettingConnection *s_con;
-    gs_unref_object NMConnection *connection = NULL;
-
-    connection = _parse_con(ARGV, "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_VLAN_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, NULL);
-}
-
-static void
-test_ibft_ip_dev_mac(void)
-{
-    const char *const *  ARGV = NM_MAKE_STRV("ip=00-53-06-66-ab-01:ibft");
-    NMSettingConnection *s_con;
-    gs_unref_object NMConnection *connection = NULL;
-
-    connection = _parse_con(ARGV, "00:53:06:66:AB:01");
-
-    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_interface_name(s_con), ==, NULL);
-}
-
-static void
-_test_ibft_ip(const char *const *ARGV)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    NMConnection *                 connection;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-
-    connection = g_hash_table_lookup(connections, "ibft0");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "iBFT VLAN Connection 0");
-    g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, NULL);
-
-    connection = g_hash_table_lookup(connections, "ibft2");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "iBFT Connection 2");
-    g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, NULL);
-}
-
-static void
-test_ibft_ip(void)
-{
-    const char *const *ARGV = NM_MAKE_STRV("ip=ibft");
-
-    _test_ibft_ip(ARGV);
-}
-
-static void
-test_ibft_rd_iscsi_ibft(void)
-{
-    const char *const *ARGV = NM_MAKE_STRV("rd.iscsi.ibft");
-
-    _test_ibft_ip(ARGV);
-}
-
-static void
-test_ignore_extra(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("blabla", "extra", "lalala");
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 0);
-}
-
-static void
-test_rd_znet(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *const       ARGV =
-        NM_MAKE_STRV("ip=10.11.12.13::10.11.12.1:24:foo.example.com:enc800:none",
-                     "ip=slc600:dhcp",
-                     "rd.znet=qeth,0.0.0800,0.0.0801,0.0.0802,layer2=0,portno=1",
-                     "rd.znet=ctc,0.0.0600,0.0.0601,layer2=0,portno=0");
-    NMConnection *          connection;
-    NMSettingConnection *   s_con;
-    NMSettingWired *        s_wired;
-    const char *const *     v_subchannels;
-    const NMUtilsNamedValue s390_options[] = {
-        {.name = "layer2", .value_str = "0"},
-        {.name = "portno", .value_str = "1"},
-    };
-    int           i_s390_options_keys;
-    gs_free char *hostname            = NULL;
-    gint64        carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-    g_assert_cmpstr(hostname, ==, "foo.example.com");
-    g_assert_cmpint(carrier_timeout_sec, ==, 0);
-
-    connection = g_hash_table_lookup(connections, "enc800");
-    g_assert(NM_IS_CONNECTION(connection));
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(NM_IS_SETTING_CONNECTION(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), ==, "enc800");
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "enc800");
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(NM_IS_SETTING_WIRED(s_wired));
-
-    v_subchannels = nm_setting_wired_get_s390_subchannels(s_wired);
-    g_assert(v_subchannels);
-    g_assert_cmpstr(v_subchannels[0], ==, "0.0.0800");
-    g_assert_cmpstr(v_subchannels[1], ==, "0.0.0801");
-    g_assert_cmpstr(v_subchannels[2], ==, "0.0.0802");
-    g_assert_cmpstr(v_subchannels[3], ==, NULL);
-
-    g_assert_cmpint(nm_setting_wired_get_num_s390_options(s_wired), ==, G_N_ELEMENTS(s390_options));
-    for (i_s390_options_keys = 0; i_s390_options_keys < G_N_ELEMENTS(s390_options);
-         i_s390_options_keys++) {
-        const NMUtilsNamedValue *s390_option = &s390_options[i_s390_options_keys];
-        const char *             k;
-        const char *             v;
-        const char *             v2;
-
-        g_assert(s390_option->name);
-        g_assert(s390_option->value_str);
-        v = nm_setting_wired_get_s390_option_by_key(s_wired, s390_option->name);
-        g_assert(v);
-        g_assert_cmpstr(v, ==, s390_option->value_str);
-
-        if (!nm_setting_wired_get_s390_option(s_wired, i_s390_options_keys, &k, &v2))
-            g_assert_not_reached();
-        g_assert_cmpstr(k, ==, s390_option->name);
-        g_assert(v == v2);
-        g_assert_cmpstr(v2, ==, s390_option->value_str);
-    }
-
-    nmtst_assert_connection_verifies_without_normalization(connection);
-
-    connection = g_hash_table_lookup(connections, "slc600");
-    g_assert(NM_IS_CONNECTION(connection));
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(NM_IS_SETTING_CONNECTION(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), ==, "slc600");
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "slc600");
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(NM_IS_SETTING_WIRED(s_wired));
-
-    v_subchannels = nm_setting_wired_get_s390_subchannels(s_wired);
-    g_assert(v_subchannels);
-    g_assert_cmpstr(v_subchannels[0], ==, "0.0.0600");
-    g_assert_cmpstr(v_subchannels[1], ==, "0.0.0601");
-    g_assert_cmpstr(v_subchannels[2], ==, NULL);
-
-    nmtst_assert_connection_verifies_without_normalization(connection);
-}
-
-static void
-test_rd_znet_legacy(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *const       ARGV =
-        NM_MAKE_STRV("ip=10.11.12.13::10.11.12.1:24:foo.example.com:eth0:none",
-                     "rd.znet=qeth,0.0.0800,0.0.0801,0.0.0802,layer2=0,portno=1",
-                     "rd.znet=ctc,0.0.0600,0.0.0601,layer2=0,portno=0",
-                     "ip=ctc0:dhcp",
-                     "net.ifnames=0");
-    NMConnection *       connection;
-    NMSettingConnection *s_con;
-    gs_free char *       hostname            = NULL;
-    gint64               carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-    g_assert_cmpstr(hostname, ==, "foo.example.com");
-    g_assert_cmpint(carrier_timeout_sec, ==, 0);
-
-    connection = g_hash_table_lookup(connections, "eth0");
-    g_assert(NM_IS_CONNECTION(connection));
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(NM_IS_SETTING_CONNECTION(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_interface_name(s_con), ==, "eth0");
-
-    nmtst_assert_connection_verifies_without_normalization(connection);
-
-    connection = g_hash_table_lookup(connections, "ctc0");
-    g_assert(NM_IS_CONNECTION(connection));
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(NM_IS_SETTING_CONNECTION(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), ==, "ctc0");
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "ctc0");
-
-    nmtst_assert_connection_verifies_without_normalization(connection);
-}
-
-static void
-test_rd_znet_no_ip(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *const       ARGV =
-        NM_MAKE_STRV("rd.znet=qeth,0.0.0800,0.0.0801,0.0.0802,layer2=0,portno=1");
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 0);
-}
-
-static void
-test_bootif_ip(void)
-{
-    const char *const *ARGV                  = NM_MAKE_STRV("BOOTIF=00:53:AB:cd:02:03", "ip=dhcp");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingWired *              s_wired;
-    NMSettingIPConfig *           s_ip4;
-    NMSettingIPConfig *           s_ip6;
-
-    connection = _parse_con(ARGV, "default_connection");
-
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "Wired Connection");
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert_cmpstr(nm_setting_wired_get_mac_address(s_wired), ==, "00:53:AB:CD:02:03");
-    g_assert(s_wired);
-
-    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(!nm_setting_ip_config_get_may_fail(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_neednet(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV        = NM_MAKE_STRV("rd.neednet",
-                                           "ip=eno1:dhcp",
-                                           "ip=172.25.1.100::172.25.1.1:24::eno2",
-                                           "bridge=br0:eno3");
-    NMConnection *                 connection;
-    NMSettingConnection *          s_con;
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 4);
-
-    connection = g_hash_table_lookup(connections, "eno1");
-    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_interface_name(s_con), ==, "eno1");
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con),
-                    ==,
-                    NMI_WAIT_DEVICE_TIMEOUT_MS);
-
-    connection = g_hash_table_lookup(connections, "eno2");
-    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_interface_name(s_con), ==, "eno2");
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con),
-                    ==,
-                    NMI_WAIT_DEVICE_TIMEOUT_MS);
-
-    connection = g_hash_table_lookup(connections, "eno3");
-    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_interface_name(s_con), ==, "eno3");
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con),
-                    ==,
-                    NMI_WAIT_DEVICE_TIMEOUT_MS);
-
-    connection = g_hash_table_lookup(connections, "br0");
-    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_interface_name(s_con), ==, "br0");
-    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
-}
-
-static void
-test_bootif_no_ip(void)
-{
-    const char *const *ARGV                  = NM_MAKE_STRV("BOOTIF=00:53:AB:cd:02:03");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingWired *              s_wired;
-    NMSettingIPConfig *           s_ip4;
-    NMSettingIPConfig *           s_ip6;
-
-    connection = _parse_con(ARGV, "default_connection");
-
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "Wired Connection");
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert_cmpstr(nm_setting_wired_get_mac_address(s_wired), ==, "00:53:AB:CD:02:03");
-    g_assert(s_wired);
-
-    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_may_fail(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_may_fail(s_ip6));
-}
-
-static void
-test_bootif_hwtype(void)
-{
-    const char *const *ARGV0  = NM_MAKE_STRV("ip=eth0:dhcp", "BOOTIF=01-00-53-AB-cd-02-03");
-    const char *const *ARGV1  = NM_MAKE_STRV("ip=eth0:dhcp", "BOOTIF=00-00-53-Ab-cD-02-03");
-    const char *const *ARGV[] = {ARGV0, ARGV1};
-    guint              i;
-
-    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
-        gs_unref_hashtable GHashTable *connections = NULL;
-        NMConnection *                 connection;
-        NMSettingWired *               s_wired;
-        NMSettingIPConfig *            s_ip4;
-        NMSettingIPConfig *            s_ip6;
-
-        connections = _parse_cons(ARGV[i]);
-        g_assert_cmpint(g_hash_table_size(connections), ==, 2);
-
-        connection = g_hash_table_lookup(connections, "eth0");
-        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(!nm_setting_wired_get_mac_address(s_wired));
-        g_assert(s_wired);
-
-        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(!nm_setting_ip_config_get_may_fail(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));
-
-        connection = g_hash_table_lookup(connections, "bootif_connection");
-        nmtst_assert_connection_verifies_without_normalization(connection);
-        g_assert_cmpstr(nm_connection_get_id(connection), ==, "BOOTIF Connection");
-
-        s_wired = nm_connection_get_setting_wired(connection);
-        g_assert_cmpstr(nm_setting_wired_get_mac_address(s_wired), ==, "00:53:AB:CD:02:03");
-        g_assert(s_wired);
-
-        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(nm_setting_ip_config_get_may_fail(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(nm_setting_ip_config_get_may_fail(s_ip6));
-    }
-}
-
-/* Check that nameservers are assigned to all existing
- * connections that support the specific IPv4/IPv6 address
- * family.
- */
-static void
-test_nameserver(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *            ARGV =
-        NM_MAKE_STRV("nameserver=1.1.1.1",
-                     "ip=eth0:dhcp",
-                     "ip=eth1:auto6",
-                     "ip=10.11.12.13::10.11.12.1:24:foo.example.com:eth2:none",
-                     "nameserver=1.0.0.1",
-                     "nameserver=[2606:4700:4700::1111]");
-    NMConnection *     connection;
-    NMSettingIPConfig *s_ip;
-    gs_free char *     hostname            = NULL;
-    gint64             carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
-    g_assert_cmpstr(hostname, ==, "foo.example.com");
-    g_assert_cmpint(carrier_timeout_sec, ==, 0);
-
-    connection = g_hash_table_lookup(connections, "eth0");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-
-    s_ip = nm_connection_get_setting_ip4_config(connection);
-    g_assert(s_ip);
-    g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip), ==, 2);
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip, 0), ==, "1.1.1.1");
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip, 1), ==, "1.0.0.1");
-
-    connection = g_hash_table_lookup(connections, "eth1");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-
-    s_ip = nm_connection_get_setting_ip6_config(connection);
-    g_assert(s_ip);
-    g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip), ==, 1);
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip, 0), ==, "2606:4700:4700::1111");
-
-    connection = g_hash_table_lookup(connections, "eth2");
-    nmtst_assert_connection_verifies_without_normalization(connection);
-
-    s_ip = nm_connection_get_setting_ip4_config(connection);
-    g_assert(s_ip);
-    g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip), ==, 2);
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip, 0), ==, "1.1.1.1");
-    g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip, 1), ==, "1.0.0.1");
-}
-
-static void
-test_bootif_off(void)
-{
-    gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *ARGV = NM_MAKE_STRV("BOOTIF=01-00-53-AB-cd-02-03", "rd.bootif=0");
-
-    connections = _parse_cons(ARGV);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 0);
-}
-
-static void
-test_dhcp_vendor_class_id(void)
-{
-    const char *const *ARGV;
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingIP4Config *          s_ip4;
-    gs_free char *                vci_long          = NULL;
-    char                          vci_arg_long[512] = {0};
-
-    ARGV       = NM_MAKE_STRV("rd.net.dhcp.vendor-class=testvci", "ip=eno1:dhcp");
-    connection = _parse_con(ARGV, "eno1");
-    s_ip4      = NM_SETTING_IP4_CONFIG(nm_connection_get_setting_ip4_config(connection));
-    g_assert_cmpstr(nm_setting_ip4_config_get_dhcp_vendor_class_identifier(s_ip4), ==, "testvci");
-
-    g_clear_object(&connection);
-
-    ARGV       = NM_MAKE_STRV("rd.net.dhcp.vendor-class", "ip=eno1:dhcp");
-    connection = _parse_con(ARGV, "eno1");
-    s_ip4      = NM_SETTING_IP4_CONFIG(nm_connection_get_setting_ip4_config(connection));
-    g_assert(nm_setting_ip4_config_get_dhcp_vendor_class_identifier(s_ip4) == NULL);
-
-    g_clear_object(&connection);
-
-    memset(vci_arg_long, 'A', 400);
-    vci_long   = g_strdup_printf("rd.net.dhcp.vendor-class=%s", vci_arg_long);
-    ARGV       = NM_MAKE_STRV(vci_long, "ip=eno1:dhcp");
-    connection = _parse_con(ARGV, "eno1");
-    s_ip4      = NM_SETTING_IP4_CONFIG(nm_connection_get_setting_ip4_config(connection));
-    g_assert(nm_setting_ip4_config_get_dhcp_vendor_class_identifier(s_ip4) == NULL);
-}
-
-static void
-test_infiniband_iface(void)
-{
-    const char *const *ARGV                  = NM_MAKE_STRV("ip=ib1:dhcp");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingInfiniband *         s_ib;
-
-    connection = _parse_con(ARGV, "ib1");
-
-    g_assert_cmpstr(nm_connection_get_connection_type(connection),
-                    ==,
-                    NM_SETTING_INFINIBAND_SETTING_NAME);
-    s_ib = nm_connection_get_setting_infiniband(connection);
-    g_assert(s_ib);
-}
-
-static void
-test_infiniband_mac(void)
-{
-    const char *const *ARGV =
-        NM_MAKE_STRV("ip=00-11-22-33-44-55-66-77-88-99-aa-bb-cc-dd-ee-ff-00-11-22-33:dhcp");
-    gs_unref_object NMConnection *connection = NULL;
-    NMSettingInfiniband *         s_ib;
-
-    connection = _parse_con(ARGV, "00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33");
-
-    g_assert_cmpstr(nm_connection_get_connection_type(connection),
-                    ==,
-                    NM_SETTING_INFINIBAND_SETTING_NAME);
-    g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, NULL);
-    s_ib = nm_connection_get_setting_infiniband(connection);
-    g_assert(s_ib);
-    g_assert_cmpstr(nm_setting_infiniband_get_mac_address(s_ib),
-                    ==,
-                    "00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33");
-}
-
-static void
-test_carrier_timeout(void)
-{
-    gs_unref_hashtable GHashTable *connections         = NULL;
-    const char *const *            ARGV                = NM_MAKE_STRV("rd.net.timeout.carrier=20");
-    gs_free char *                 hostname            = NULL;
-    gint64                         carrier_timeout_sec = 0;
-
-    connections = _parse(ARGV, &hostname, &carrier_timeout_sec);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 0);
-    g_assert_cmpstr(hostname, ==, NULL);
-    g_assert_cmpint(carrier_timeout_sec, ==, 20);
-}
-
-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/dhcp_with_hostname", test_dhcp_with_hostname);
-    g_test_add_func("/initrd/cmdline/dhcp_with_mtu", test_dhcp_with_mtu);
-    g_test_add_func("/initrd/cmdline/dhcp_timeout", test_dhcp_timeout);
-    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/if_mac_ifname", test_if_mac_ifname);
-    g_test_add_func("/initrd/cmdline/if_off", test_if_off);
-    g_test_add_func("/initrd/cmdline/multiple/merge", test_multiple_merge);
-    g_test_add_func("/initrd/cmdline/multiple/bootdev", test_multiple_bootdev);
-    g_test_add_func("/initrd/cmdline/nameserver", test_nameserver);
-    g_test_add_func("/initrd/cmdline/some_more", test_some_more);
-    g_test_add_func("/initrd/cmdline/bootdev", test_bootdev);
-    g_test_add_func("/initrd/cmdline/bond", test_bond);
-    g_test_add_func("/initrd/cmdline/bond/ip", test_bond_ip);
-    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/vlan", test_vlan);
-    g_test_add_func("/initrd/cmdline/vlan/dhcp-on-parent", test_vlan_with_dhcp_on_parent);
-    g_test_add_func("/initrd/cmdline/vlan/over-bond", test_vlan_over_bond);
-    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/bridge/ip", test_bridge_ip);
-    g_test_add_func("/initrd/cmdline/ibft/ip_dev", test_ibft_ip_dev);
-    g_test_add_func("/initrd/cmdline/ibft/ip_dev_mac", test_ibft_ip_dev_mac);
-    g_test_add_func("/initrd/cmdline/ibft/ip", test_ibft_ip);
-    g_test_add_func("/initrd/cmdline/ibft/rd_iscsi_ibft", test_ibft_rd_iscsi_ibft);
-    g_test_add_func("/initrd/cmdline/ignore_extra", test_ignore_extra);
-    g_test_add_func("/initrd/cmdline/rd_znet", test_rd_znet);
-    g_test_add_func("/initrd/cmdline/rd_znet/legacy", test_rd_znet_legacy);
-    g_test_add_func("/initrd/cmdline/rd_znet/no_ip", test_rd_znet_no_ip);
-    g_test_add_func("/initrd/cmdline/bootif/ip", test_bootif_ip);
-    g_test_add_func("/initrd/cmdline/bootif/no_ip", test_bootif_no_ip);
-    g_test_add_func("/initrd/cmdline/bootif/hwtype", test_bootif_hwtype);
-    g_test_add_func("/initrd/cmdline/bootif/off", test_bootif_off);
-    g_test_add_func("/initrd/cmdline/neednet", test_neednet);
-    g_test_add_func("/initrd/cmdline/dhcp/vendor_class_id", test_dhcp_vendor_class_id);
-    g_test_add_func("/initrd/cmdline/infiniband/iface", test_infiniband_iface);
-    g_test_add_func("/initrd/cmdline/infiniband/mac", test_infiniband_mac);
-    g_test_add_func("/initrd/cmdline/carrier_timeout", test_carrier_timeout);
-
-    return g_test_run();
-}
diff --git a/src/core/initrd/tests/test-dt-reader.c b/src/core/initrd/tests/test-dt-reader.c
deleted file mode 100644
index 619a4e6f..00000000
--- a/src/core/initrd/tests/test-dt-reader.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2014 - 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <unistd.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"
-
-#define TEST_INITRD_DIR NM_BUILD_SRCDIR "/src/core/initrd/tests"
-
-/*****************************************************************************/
-
-static void
-test_read_dt_ofw(void)
-{
-    NMConnection *       connection;
-    NMSettingConnection *s_con;
-    NMSettingWired *     s_wired;
-    NMSettingIPConfig *  s_ip4;
-    NMSettingIPConfig *  s_ip6;
-    const char *         mac_address;
-
-    connection = nmi_dt_reader_parse(TEST_INITRD_DIR "/sysfs-dt");
-    g_assert(connection);
-    nmtst_assert_connection_verifies(connection);
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
-                    ==,
-                    NM_SETTING_WIRED_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "OpenFirmware Connection");
-    g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0);
-    g_assert(nm_setting_connection_get_autoconnect(s_con));
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(s_wired);
-    mac_address = nm_setting_wired_get_mac_address(s_wired);
-    g_assert(mac_address);
-    g_assert(nm_utils_hwaddr_matches(mac_address, -1, "ac:7f:3e:e5:d8:d8", -1));
-    g_assert(!nm_setting_wired_get_duplex(s_wired));
-    g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 0);
-    g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 0);
-
-    s_ip4 = nm_connection_get_setting_ip4_config(connection);
-    g_assert(s_ip4);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
-    g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip4), ==, "demiurge");
-
-    s_ip6 = nm_connection_get_setting_ip6_config(connection);
-    g_assert(s_ip6);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
-
-    g_object_unref(connection);
-}
-
-static void
-test_read_dt_slof(void)
-{
-    NMConnection *       connection;
-    NMSettingConnection *s_con;
-    NMSettingWired *     s_wired;
-    NMSettingIPConfig *  s_ip4;
-    NMSettingIPConfig *  s_ip6;
-    NMIPAddress *        ip4_addr;
-
-    connection = nmi_dt_reader_parse(TEST_INITRD_DIR "/sysfs-dt-tftp");
-    g_assert(connection);
-    nmtst_assert_connection_verifies(connection);
-
-    s_con = nm_connection_get_setting_connection(connection);
-    g_assert(s_con);
-    g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
-                    ==,
-                    NM_SETTING_WIRED_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "OpenFirmware Connection");
-    g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0);
-    g_assert(nm_setting_connection_get_autoconnect(s_con));
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    g_assert(s_wired);
-    g_assert(!nm_setting_wired_get_mac_address(s_wired));
-    g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, "half");
-    g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 10);
-    g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 0);
-
-    s_ip4 = nm_connection_get_setting_ip4_config(connection);
-    g_assert(s_ip4);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
-                    ==,
-                    NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
-
-    g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1);
-    ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0);
-    g_assert(ip4_addr);
-    g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "192.168.32.2");
-    g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 16);
-
-    g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "192.168.32.1");
-
-    s_ip6 = nm_connection_get_setting_ip6_config(connection);
-    g_assert(s_ip6);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
-                    ==,
-                    NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
-
-    g_object_unref(connection);
-}
-
-static void
-test_read_dt_none(void)
-{
-    NMConnection *connection;
-
-    connection = nmi_dt_reader_parse(TEST_INITRD_DIR "/sysfs");
-    g_assert(!connection);
-}
-
-NMTST_DEFINE();
-
-int
-main(int argc, char **argv)
-{
-    nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT");
-
-    g_test_add_func("/initrd/dt/ofw", test_read_dt_ofw);
-    g_test_add_func("/initrd/dt/slof", test_read_dt_slof);
-    g_test_add_func("/initrd/dt/none", test_read_dt_none);
-
-    return g_test_run();
-}
diff --git a/src/core/initrd/tests/test-ibft-reader.c b/src/core/initrd/tests/test-ibft-reader.c
deleted file mode 100644
index 1d263b9e..00000000
--- a/src/core/initrd/tests/test-ibft-reader.c
+++ /dev/null
@@ -1,303 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2014 - 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <unistd.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"
-
-#define TEST_INITRD_DIR NM_BUILD_SRCDIR "/src/core/initrd/tests"
-
-/*****************************************************************************/
-
-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_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, NULL);
-    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_DISABLED);
-
-    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_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, NULL);
-    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_DISABLED);
-
-    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);
-    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, NULL);
-
-    /* ===== 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_DISABLED);
-    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/core/main.c b/src/core/main.c
index 9b79bcbb..cfcdb860 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -12,6 +12,7 @@
 #include <signal.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <linux/if.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/resource.h>
@@ -20,7 +21,7 @@
 #include "nm-dbus-interface.h"
 #include "NetworkManagerUtils.h"
 #include "nm-manager.h"
-#include "platform/nm-linux-platform.h"
+#include "libnm-platform/nm-linux-platform.h"
 #include "nm-dbus-manager.h"
 #include "devices/nm-device.h"
 #include "dhcp/nm-dhcp-manager.h"
@@ -29,11 +30,11 @@
 #include "nm-dispatcher.h"
 #include "settings/nm-settings.h"
 #include "nm-auth-manager.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-object.h"
 #include "nm-connectivity.h"
 #include "dns/nm-dns-manager.h"
-#include "systemd/nm-sd.h"
+#include "libnm-systemd-core/nm-sd.h"
 #include "nm-netns.h"
 
 #if !defined(NM_DIST_VERSION)
@@ -508,7 +509,7 @@ main(int argc, char *argv[])
      * physical interfaces.
      */
     nm_log_dbg(LOGD_CORE, "setting up local loopback");
-    nm_platform_link_set_up(NM_PLATFORM_GET, 1, NULL);
+    nm_platform_link_change_flags(NM_PLATFORM_GET, 1, IFF_UP, TRUE);
 
     success = TRUE;
 
diff --git a/src/core/meson.build b/src/core/meson.build
index 368a3e85..f4d7af64 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -1,27 +1,27 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-src_inc = include_directories('.')
+src_core_inc = include_directories('.')
 
 core_plugin_dep = declare_dependency(
-  sources: libnm_core_enum_sources[1],
   include_directories: [
-    src_inc,
+    src_core_inc,
     top_inc,
-    shared_inc,
-    libnm_core_inc,
+    libnm_core_public_inc,
   ],
   dependencies: [
+    libnm_core_public_dep,
     glib_dep,
   ],
 )
 
 core_default_dep = declare_dependency(
-  sources: libnm_core_enum_sources[1],
-  include_directories: src_inc,
+  include_directories: [
+    src_core_inc,
+    src_inc,
+    top_inc,
+  ],
   dependencies: [
-    libnm_core_nm_default_dep,
-    libnm_log_core_dep,
-    libnm_platform_dep,
+    libnm_core_public_dep,
   ],
 )
 
@@ -30,19 +30,8 @@ install_data(
   install_dir: dbus_conf_dir,
 )
 
-subdir('systemd')
-
 core_plugins = []
 
-daemon_c_flags = [
-  '-DG_LOG_DOMAIN="NetworkManager"',
-]
-
-platform_wifi_wext_source = files()
-if enable_wext
-  platform_wifi_wext_source += files('platform/wifi/nm-wifi-utils-wext.c')
-endif
-
 libNetworkManagerBase = static_library(
   'NetworkManagerBase',
   sources: files(
@@ -54,13 +43,6 @@ libNetworkManagerBase = static_library(
     'dhcp/nm-dhcp-options.c',
     'ndisc/nm-lndp-ndisc.c',
     'ndisc/nm-ndisc.c',
-    'platform/wifi/nm-wifi-utils-nl80211.c',
-    'platform/wifi/nm-wifi-utils.c',
-    'platform/wpan/nm-wpan-utils.c',
-    'platform/nm-linux-platform.c',
-    'platform/nm-platform.c',
-    'platform/nmp-object.c',
-    'platform/nmp-rules-manager.c',
     'main-utils.c',
     'NetworkManagerUtils.c',
     'nm-core-utils.c',
@@ -73,23 +55,19 @@ libNetworkManagerBase = static_library(
     'nm-ip-config.c',
     'nm-ip4-config.c',
     'nm-ip6-config.c',
-  ) + platform_wifi_wext_source,
+  ),
   dependencies: [
     core_default_dep,
-    libn_dhcp4_dep,
-    libnm_core_dep,
-    libnm_systemd_shared_dep,
-    libnm_udev_aux_dep,
+    libnm_core_public_dep,
     libsystemd_dep,
     libudev_dep,
   ],
-  c_args: daemon_c_flags,
 )
 
 nm_deps = [
+  libnm_core_public_dep,
   core_default_dep,
   dl_dep,
-  libn_acd_dep,
   libndp_dep,
   libudev_dep,
   logind_dep,
@@ -119,6 +97,7 @@ libNetworkManager = static_library(
     'devices/nm-device-bond.c',
     'devices/nm-device-bridge.c',
     'devices/nm-device.c',
+    'devices/nm-device-utils.c',
     'devices/nm-device-dummy.c',
     'devices/nm-device-ethernet.c',
     'devices/nm-device-ethernet-utils.c',
@@ -182,7 +161,8 @@ libNetworkManager = static_library(
     'nm-dcb.c',
     'nm-dhcp-config.c',
     'nm-dispatcher.c',
-    'nm-firewall-manager.c',
+    'nm-firewall-utils.c',
+    'nm-firewalld-manager.c',
     'nm-hostname-manager.c',
     'nm-keep-alive.c',
     'nm-manager.c',
@@ -194,7 +174,6 @@ libNetworkManager = static_library(
     'nm-sleep-monitor.c',
   ),
   dependencies: nm_deps,
-  c_args: daemon_c_flags,
   link_with: [
     libNetworkManagerBase,
     libnm_systemd_core,
@@ -207,16 +186,28 @@ executable(
   'nm-iface-helper.c',
   dependencies: [
     core_default_dep,
-    dl_dep,
-    libndp_dep,
+    glib_dep,
     libudev_dep,
-    libn_acd_dep,
+    libndp_dep,
+    dl_dep,
   ],
-  c_args: daemon_c_flags,
   link_with: [
     libNetworkManagerBase,
+    libnm_core_aux_intern,
+    libnm_core_impl,
+    libnm_crypto,
     libnm_systemd_core,
+    libnm_platform,
     libnm_systemd_shared,
+    libnm_base,
+    libnm_udev_aux,
+    libnm_log_core,
+    libnm_glib_aux,
+    libnm_std_aux,
+    libn_acd,
+    libn_dhcp4,
+    libc_rbtree,
+    libc_siphash,
   ],
   link_args: ldflags_linker_script_binary,
   link_depends: linker_script_binary,
@@ -225,7 +216,7 @@ executable(
 )
 
 if enable_tests
-  test_c_flags = daemon_c_flags
+  test_c_flags = []
   if require_root_tests
     test_c_flags += ['-DREQUIRE_ROOT_TESTS=1']
   endif
@@ -244,7 +235,23 @@ if enable_tests
 
   libNetworkManagerTest_dep = declare_dependency(
     dependencies: core_default_dep,
-    link_with: libNetworkManagerTest,
+    link_with: [
+      libNetworkManagerTest,
+      libnm_core_aux_intern,
+      libnm_core_impl,
+      libnm_crypto,
+      libnm_platform,
+      libnm_base,
+      libnm_systemd_shared,
+      libnm_udev_aux,
+      libnm_log_core,
+      libnm_glib_aux,
+      libnm_std_aux,
+      libn_acd,
+      libn_dhcp4,
+      libc_siphash,
+      libc_rbtree,
+    ],
   )
 
   subdir('dnsmasq/tests')
@@ -261,7 +268,6 @@ if enable_ppp
 endif
 
 subdir('devices')
-subdir('initrd')
 subdir('settings/plugins')
 
 # NetworkManager binary
@@ -274,13 +280,27 @@ subdir('settings/plugins')
 NetworkManager_all_sym = executable(
   'NetworkManager-all-sym',
   'main.c',
-  dependencies: nm_deps,
-  c_args: daemon_c_flags,
+  dependencies: [
+    nm_deps,
+    libudev_dep,
+  ],
   link_args: '-Wl,--no-gc-sections',
   link_whole: [
     libNetworkManager,
     libNetworkManagerBase,
-    libnm_core,
+    libnm_core_aux_intern,
+    libnm_core_impl,
+    libnm_crypto,
+    libnm_platform,
+    libnm_base,
+    libnm_log_core,
+    libnm_udev_aux,
+    libnm_glib_aux,
+    libnm_std_aux,
+    libn_acd,
+    libn_dhcp4,
+    libc_rbtree,
+    libc_siphash,
   ],
 )
 
@@ -301,9 +321,27 @@ ver_script = custom_target(
 NetworkManager = executable(
   'NetworkManager',
   'main.c',
-  dependencies: nm_deps,
-  c_args: daemon_c_flags,
-  link_with: libNetworkManager,
+  dependencies: [
+    nm_deps,
+    libudev_dep,
+  ],
+  link_with: [
+    libNetworkManager,
+    libNetworkManagerBase,
+    libnm_core_aux_intern,
+    libnm_core_impl,
+    libnm_crypto,
+    libnm_platform,
+    libnm_base,
+    libnm_udev_aux,
+    libnm_log_core,
+    libnm_glib_aux,
+    libnm_std_aux,
+    libn_acd,
+    libn_dhcp4,
+    libc_rbtree,
+    libc_siphash,
+  ],
   link_args: [
     '-rdynamic',
     '-Wl,--version-script,@0@'.format(ver_script.full_path()),
diff --git a/src/core/ndisc/nm-lndp-ndisc.c b/src/core/ndisc/nm-lndp-ndisc.c
index f773478f..f11178f8 100644
--- a/src/core/ndisc/nm-lndp-ndisc.c
+++ b/src/core/ndisc/nm-lndp-ndisc.c
@@ -13,12 +13,12 @@
 #include <stdarg.h>
 #include <ndp.h>
 
-#include "nm-glib-aux/nm-str-buf.h"
-#include "systemd/nm-sd-utils-shared.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 #include "nm-ndisc-private.h"
 #include "NetworkManagerUtils.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nmp-netns.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-netns.h"
 
 #define _NMLOG_PREFIX_NAME "ndisc-lndp"
 
@@ -85,14 +85,14 @@ send_rs(NMNDisc *ndisc, GError **error)
 static NMIcmpv6RouterPref
 _route_preference_coerce(enum ndp_route_preference pref)
 {
-#define _ASSERT_ENUM(v1, v2)                                      \
-    G_STMT_START                                                  \
-    {                                                             \
-        G_STATIC_ASSERT((NMIcmpv6RouterPref)(v1) == (v2));        \
-        G_STATIC_ASSERT((enum ndp_route_preference)(v2) == (v1)); \
-        G_STATIC_ASSERT((gint64)(v1) == (v2));                    \
-        G_STATIC_ASSERT((gint64)(v2) == (v1));                    \
-    }                                                             \
+#define _ASSERT_ENUM(v1, v2)                                       \
+    G_STMT_START                                                   \
+    {                                                              \
+        G_STATIC_ASSERT((NMIcmpv6RouterPref) (v1) == (v2));        \
+        G_STATIC_ASSERT((enum ndp_route_preference) (v2) == (v1)); \
+        G_STATIC_ASSERT((gint64) (v1) == (v2));                    \
+        G_STATIC_ASSERT((gint64) (v2) == (v1));                    \
+    }                                                              \
     G_STMT_END
 
     switch (pref) {
diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c
index a5c1b935..cf1b58ec 100644
--- a/src/core/ndisc/nm-ndisc.c
+++ b/src/core/ndisc/nm-ndisc.c
@@ -14,8 +14,8 @@
 
 #include "nm-ndisc-private.h"
 #include "nm-utils.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nmp-netns.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-netns.h"
 #include "nm-l3-config-data.h"
 
 #define _NMLOG_PREFIX_NAME "ndisc"
@@ -806,7 +806,7 @@ solicit_retransmit_time_jitter(gint32 solicit_retransmit_time_msec)
     ten_percent = NM_MAX(1, solicit_retransmit_time_msec / 10);
 
     return solicit_retransmit_time_msec - ten_percent
-           + ((gint32)(g_random_int() % (2u * ((guint32) ten_percent))));
+           + ((gint32) (g_random_int() % (2u * ((guint32) ten_percent))));
 }
 
 static gboolean
@@ -882,7 +882,7 @@ solicit_timer_start(NMNDisc *ndisc)
      * a suitable delay in 2021. Wait only up to 250 msec instead. */
 
     delay_msec =
-        g_random_int() % ((guint32)(NM_NDISC_RFC4861_MAX_RTR_SOLICITATION_DELAY * 1000 / 4));
+        g_random_int() % ((guint32) (NM_NDISC_RFC4861_MAX_RTR_SOLICITATION_DELAY * 1000 / 4));
 
     _LOGD("solicit: schedule sending first solicitation (of %d) in %.3f seconds",
           priv->router_solicitations,
diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h
index 93aee4db..15f95d20 100644
--- a/src/core/ndisc/nm-ndisc.h
+++ b/src/core/ndisc/nm-ndisc.h
@@ -13,8 +13,8 @@
 #include "nm-setting-ip6-config.h"
 #include "NetworkManagerUtils.h"
 
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
 
 #define NM_RA_TIMEOUT_DEFAULT  ((guint32) 0)
 #define NM_RA_TIMEOUT_INFINITY ((guint32) G_MAXINT32)
@@ -99,7 +99,7 @@ _nm_ndisc_lifetime_from_expiry(gint64 now_msec, gint64 expiry_msec, gboolean cei
         diff += 999;
     }
 
-    return NM_MIN(diff / 1000, (gint64)(G_MAXUINT32 - 1));
+    return NM_MIN(diff / 1000, (gint64) (G_MAXUINT32 - 1));
 }
 
 /*****************************************************************************/
@@ -159,10 +159,10 @@ typedef enum {
 #define NM_NDISC_RFC4861_MAX_RTR_SOLICITATION_DELAY 1 /* seconds */
 
 #define NM_NDISC_MAX_ADDRESSES_DEFAULT          16
-#define NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT   3 /* RFC4861, MAX_RTR_SOLICITATIONS */
-#define NM_NDISC_ROUTER_ADVERTISEMENTS_DEFAULT  3 /* RFC4861, MAX_INITIAL_RTR_ADVERTISEMENTS */
-#define NM_NDISC_ROUTER_ADVERT_DELAY            3 /* RFC4861, MIN_DELAY_BETWEEN_RAS */
-#define NM_NDISC_ROUTER_ADVERT_INITIAL_INTERVAL 16 /* RFC4861, MAX_INITIAL_RTR_ADVERT_INTERVAL */
+#define NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT   3   /* RFC4861, MAX_RTR_SOLICITATIONS */
+#define NM_NDISC_ROUTER_ADVERTISEMENTS_DEFAULT  3   /* RFC4861, MAX_INITIAL_RTR_ADVERTISEMENTS */
+#define NM_NDISC_ROUTER_ADVERT_DELAY            3   /* RFC4861, MIN_DELAY_BETWEEN_RAS */
+#define NM_NDISC_ROUTER_ADVERT_INITIAL_INTERVAL 16  /* RFC4861, MAX_INITIAL_RTR_ADVERT_INTERVAL */
 #define NM_NDISC_ROUTER_ADVERT_DELAY_MS         500 /* RFC4861, MAX_RA_DELAY_TIME */
 #define NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL     600 /* RFC4861, MaxRtrAdvInterval default */
 #define NM_NDISC_ROUTER_LIFETIME                900 /* 1.5 * NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL */
diff --git a/src/core/ndisc/tests/test-ndisc-linux.c b/src/core/ndisc/tests/test-ndisc-linux.c
index 9a5df60b..c84ee142 100644
--- a/src/core/ndisc/tests/test-ndisc-linux.c
+++ b/src/core/ndisc/tests/test-ndisc-linux.c
@@ -10,7 +10,7 @@
 #include "ndisc/nm-ndisc.h"
 #include "ndisc/nm-lndp-ndisc.h"
 
-#include "platform/nm-linux-platform.h"
+#include "libnm-platform/nm-linux-platform.h"
 
 #include "nm-test-utils-core.h"
 
diff --git a/src/core/nm-act-request.c b/src/core/nm-act-request.c
index defe3e8c..d0a2e0c7 100644
--- a/src/core/nm-act-request.c
+++ b/src/core/nm-act-request.c
@@ -13,17 +13,19 @@
 #include <unistd.h>
 
 #include "c-list/src/c-list.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 
-#include "nm-setting-wireless-security.h"
 #include "nm-setting-8021x.h"
+#include "nm-setting-wireless-security.h"
+
 #include "devices/nm-device.h"
 #include "nm-active-connection.h"
+#include "nm-firewall-utils.h"
 #include "settings/nm-settings-connection.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
 
 typedef struct {
-    CList              call_ids_lst_head;
-    NMUtilsShareRules *share_rules;
+    CList             call_ids_lst_head;
+    NMFirewallConfig *firewall_config;
 } NMActRequestPrivate;
 
 struct _NMActRequest {
@@ -248,31 +250,31 @@ nm_act_request_clear_secrets(NMActRequest *self)
 
 /*****************************************************************************/
 
-NMUtilsShareRules *
+NMFirewallConfig *
 nm_act_request_get_shared(NMActRequest *req)
 {
     g_return_val_if_fail(NM_IS_ACT_REQUEST(req), FALSE);
 
-    return NM_ACT_REQUEST_GET_PRIVATE(req)->share_rules;
+    return NM_ACT_REQUEST_GET_PRIVATE(req)->firewall_config;
 }
 
 void
-nm_act_request_set_shared(NMActRequest *req, NMUtilsShareRules *rules)
+nm_act_request_set_shared(NMActRequest *req, NMFirewallConfig *rules)
 {
     NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req);
 
     g_return_if_fail(NM_IS_ACT_REQUEST(req));
 
-    if (priv->share_rules == rules)
+    if (priv->firewall_config == rules)
         return;
 
-    if (priv->share_rules) {
-        nm_utils_share_rules_apply(priv->share_rules, FALSE);
-        priv->share_rules = NULL;
+    if (priv->firewall_config) {
+        nm_firewall_config_apply(priv->firewall_config, FALSE);
+        priv->firewall_config = NULL;
     }
     if (rules) {
-        priv->share_rules = rules;
-        nm_utils_share_rules_apply(priv->share_rules, TRUE);
+        priv->firewall_config = rules;
+        nm_firewall_config_apply(priv->firewall_config, TRUE);
     }
 }
 
@@ -506,9 +508,9 @@ dispose(GObject *object)
     c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst)
         _do_cancel_secrets(self, call_id, TRUE);
 
-    if (priv->share_rules) {
-        nm_utils_share_rules_apply(priv->share_rules, FALSE);
-        nm_clear_pointer(&priv->share_rules, nm_utils_share_rules_free);
+    if (priv->firewall_config) {
+        nm_firewall_config_apply(priv->firewall_config, FALSE);
+        nm_clear_pointer(&priv->firewall_config, nm_firewall_config_free);
     }
 
     G_OBJECT_CLASS(nm_act_request_parent_class)->dispose(object);
diff --git a/src/core/nm-act-request.h b/src/core/nm-act-request.h
index 2a568430..9c702d31 100644
--- a/src/core/nm-act-request.h
+++ b/src/core/nm-act-request.h
@@ -38,11 +38,11 @@ NMConnection *nm_act_request_get_applied_connection(NMActRequest *req);
 
 /*****************************************************************************/
 
-struct _NMUtilsShareRules;
+struct _NMFirewallConfig;
 
-struct _NMUtilsShareRules *nm_act_request_get_shared(NMActRequest *req);
+struct _NMFirewallConfig *nm_act_request_get_shared(NMActRequest *req);
 
-void nm_act_request_set_shared(NMActRequest *req, struct _NMUtilsShareRules *rules);
+void nm_act_request_set_shared(NMActRequest *req, struct _NMFirewallConfig *rules);
 
 /*****************************************************************************/
 
diff --git a/src/core/nm-active-connection.c b/src/core/nm-active-connection.c
index 3c8c472e..bf2c4a39 100644
--- a/src/core/nm-active-connection.c
+++ b/src/core/nm-active-connection.c
@@ -7,17 +7,17 @@
 
 #include "nm-active-connection.h"
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-dbus-interface.h"
 #include "devices/nm-device.h"
 #include "settings/nm-settings-connection.h"
 #include "nm-simple-connection.h"
 #include "nm-auth-utils.h"
 #include "nm-auth-manager.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-keep-alive.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED ((NMAuthManagerCallId *) GINT_TO_POINTER(1))
 
@@ -516,11 +516,6 @@ nm_active_connection_clear_secrets(NMActiveConnection *self)
 
     priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self);
 
-    if (nm_settings_connection_has_unmodified_applied_connection(priv->settings_connection.obj,
-                                                                 priv->applied_connection,
-                                                                 NM_SETTING_COMPARE_FLAG_NONE)) {
-        nm_settings_connection_clear_secrets(priv->settings_connection.obj, FALSE, FALSE);
-    }
     nm_connection_clear_secrets(priv->applied_connection);
 }
 
@@ -1560,54 +1555,48 @@ static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_
 static const NMDBusInterfaceInfoExtended interface_info_active_connection = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_ACTIVE_CONNECTION,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_state_changed, ),
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_state_changed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Connection",
-                                                             "o",
-                                                             NM_ACTIVE_CONNECTION_CONNECTION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("SpecificObject",
-                                                             "o",
-                                                             NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Id", "s", NM_ACTIVE_CONNECTION_ID),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Uuid",
-                                                             "s",
-                                                             NM_ACTIVE_CONNECTION_UUID),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Type",
-                                                             "s",
-                                                             NM_ACTIVE_CONNECTION_TYPE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Devices",
-                                                             "ao",
-                                                             NM_ACTIVE_CONNECTION_DEVICES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("State",
-                                                             "u",
-                                                             NM_ACTIVE_CONNECTION_STATE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("StateFlags",
-                                                             "u",
-                                                             NM_ACTIVE_CONNECTION_STATE_FLAGS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Default",
-                                                             "b",
-                                                             NM_ACTIVE_CONNECTION_DEFAULT),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ip4Config",
-                                                             "o",
-                                                             NM_ACTIVE_CONNECTION_IP4_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Dhcp4Config",
-                                                             "o",
-                                                             NM_ACTIVE_CONNECTION_DHCP4_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Default6",
-                                                             "b",
-                                                             NM_ACTIVE_CONNECTION_DEFAULT6),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ip6Config",
-                                                             "o",
-                                                             NM_ACTIVE_CONNECTION_IP6_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Dhcp6Config",
-                                                             "o",
-                                                             NM_ACTIVE_CONNECTION_DHCP6_CONFIG),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Vpn", "b", NM_ACTIVE_CONNECTION_VPN),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Master",
-                                                             "o",
-                                                             NM_ACTIVE_CONNECTION_MASTER), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Connection",
+                                                           "o",
+                                                           NM_ACTIVE_CONNECTION_CONNECTION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("SpecificObject",
+                                                           "o",
+                                                           NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Id", "s", NM_ACTIVE_CONNECTION_ID),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Uuid", "s", NM_ACTIVE_CONNECTION_UUID),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Type", "s", NM_ACTIVE_CONNECTION_TYPE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Devices",
+                                                           "ao",
+                                                           NM_ACTIVE_CONNECTION_DEVICES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("State",
+                                                           "u",
+                                                           NM_ACTIVE_CONNECTION_STATE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("StateFlags",
+                                                           "u",
+                                                           NM_ACTIVE_CONNECTION_STATE_FLAGS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Default",
+                                                           "b",
+                                                           NM_ACTIVE_CONNECTION_DEFAULT),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ip4Config",
+                                                           "o",
+                                                           NM_ACTIVE_CONNECTION_IP4_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Dhcp4Config",
+                                                           "o",
+                                                           NM_ACTIVE_CONNECTION_DHCP4_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Default6",
+                                                           "b",
+                                                           NM_ACTIVE_CONNECTION_DEFAULT6),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ip6Config",
+                                                           "o",
+                                                           NM_ACTIVE_CONNECTION_IP6_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Dhcp6Config",
+                                                           "o",
+                                                           NM_ACTIVE_CONNECTION_DHCP6_CONFIG),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Vpn", "b", NM_ACTIVE_CONNECTION_VPN),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Master",
+                                                           "o",
+                                                           NM_ACTIVE_CONNECTION_MASTER), ), ),
 };
 
 static void
diff --git a/src/core/nm-audit-manager.c b/src/core/nm-audit-manager.c
index 8f419472..2ef5d860 100644
--- a/src/core/nm-audit-manager.c
+++ b/src/core/nm-audit-manager.c
@@ -11,14 +11,18 @@
     #include <libaudit.h>
 #endif
 
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#define NM_VALUE_TYPE_DEFINE_FUNCTIONS
+
+#include "libnm-core-aux-intern/nm-auth-subject.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-value-type.h"
 #include "nm-config.h"
 #include "nm-dbus-manager.h"
 #include "settings/nm-settings-connection.h"
 
 /*****************************************************************************/
 
-typedef enum {
+typedef enum _nm_packed {
     BACKEND_LOG    = (1 << 0),
     BACKEND_AUDITD = (1 << 1),
     _BACKEND_LAST,
@@ -26,10 +30,11 @@ typedef enum {
 } AuditBackend;
 
 typedef struct {
-    const char * name;
-    GValue       value;
-    gboolean     need_encoding;
-    AuditBackend backends;
+    const char *    name;
+    AuditBackend    backends;
+    bool            need_encoding;
+    NMValueType     value_type;
+    NMValueTypUnion value;
 } AuditField;
 
 /*****************************************************************************/
@@ -85,66 +90,77 @@ _audit_field_init_string(AuditField * field,
                          gboolean     need_encoding,
                          AuditBackend backends)
 {
-    field->name          = name;
-    field->need_encoding = need_encoding;
-    field->backends      = backends;
-    g_value_init(&field->value, G_TYPE_STRING);
-    g_value_set_static_string(&field->value, str);
+    *field = (AuditField){
+        .name           = name,
+        .need_encoding  = need_encoding,
+        .backends       = backends,
+        .value_type     = NM_VALUE_TYPE_STRING,
+        .value.v_string = str,
+    };
 }
 
 static void
-_audit_field_init_uint(AuditField *field, const char *name, uint val, AuditBackend backends)
+_audit_field_init_uint64(AuditField *field, const char *name, guint64 val, AuditBackend backends)
 {
-    field->name     = name;
-    field->backends = backends;
-    g_value_init(&field->value, G_TYPE_UINT);
-    g_value_set_uint(&field->value, val);
+    *field = (AuditField){
+        .name           = name,
+        .backends       = backends,
+        .value_type     = NM_VALUE_TYPE_UINT64,
+        .value.v_uint64 = val,
+    };
 }
 
-static char *
-build_message(GPtrArray *fields, AuditBackend backend)
+static const char *
+build_message(NMStrBuf *strbuf, AuditBackend backend, GPtrArray *fields)
 {
-    GString *   string;
-    AuditField *field;
-    gboolean    first = TRUE;
-    guint       i;
+    guint i;
 
-    string = g_string_new(NULL);
+    if (strbuf->len == 0) {
+        /* preallocate a large buffer... */
+        nm_str_buf_maybe_expand(strbuf, NM_UTILS_GET_NEXT_REALLOC_SIZE_232, FALSE);
+    } else
+        nm_str_buf_reset(strbuf);
 
     for (i = 0; i < fields->len; i++) {
-        field = fields->pdata[i];
+        const AuditField *field = fields->pdata[i];
 
         if (!NM_FLAGS_ANY(field->backends, backend))
             continue;
 
-        if (first)
-            first = FALSE;
-        else
-            g_string_append_c(string, ' ');
+        nm_str_buf_append_required_delimiter(strbuf, ' ');
 
-        if (G_VALUE_HOLDS_STRING(&field->value)) {
-            const char *str = g_value_get_string(&field->value);
+        if (field->value_type == NM_VALUE_TYPE_STRING) {
+            const char *str = field->value.v_string;
 
 #if HAVE_LIBAUDIT
             if (backend == BACKEND_AUDITD) {
                 if (field->need_encoding) {
-                    char *value;
+                    gs_free char *value = NULL;
 
                     value = audit_encode_nv_string(field->name, str, 0);
-                    g_string_append(string, value);
-                    g_free(value);
+                    nm_str_buf_append(strbuf, value);
                 } else
-                    g_string_append_printf(string, "%s=%s", field->name, str);
+                    nm_str_buf_append_printf(strbuf, "%s=%s", field->name, str);
                 continue;
             }
 #endif /* HAVE_LIBAUDIT */
-            g_string_append_printf(string, "%s=\"%s\"", field->name, str);
-        } else if (G_VALUE_HOLDS_UINT(&field->value)) {
-            g_string_append_printf(string, "%s=%u", field->name, g_value_get_uint(&field->value));
-        } else
-            g_assert_not_reached();
+
+            nm_str_buf_append_printf(strbuf, "%s=\"%s\"", field->name, str);
+            continue;
+        }
+
+        if (field->value_type == NM_VALUE_TYPE_UINT64) {
+            nm_str_buf_append_printf(strbuf,
+                                     "%s=%" G_GUINT64_FORMAT,
+                                     field->name,
+                                     field->value.v_uint64);
+            continue;
+        }
+
+        g_return_val_if_reached(NULL);
     }
-    return g_string_free(string, FALSE);
+
+    return nm_str_buf_get_str(strbuf);
 }
 
 static void
@@ -155,10 +171,10 @@ nm_audit_log(NMAuditManager *self,
              const char *    func,
              gboolean        success)
 {
+    nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(0, FALSE);
 #if HAVE_LIBAUDIT
     NMAuditManagerPrivate *priv;
 #endif
-    char *msg;
 
     g_return_if_fail(NM_IS_AUDIT_MANAGER(self));
 
@@ -166,16 +182,29 @@ nm_audit_log(NMAuditManager *self,
     priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
 
     if (priv->auditd_fd >= 0) {
-        msg = build_message(fields, BACKEND_AUDITD);
-        audit_log_user_message(priv->auditd_fd, AUDIT_USYS_CONFIG, msg, NULL, NULL, NULL, success);
-        g_free(msg);
+        audit_log_user_message(priv->auditd_fd,
+                               AUDIT_USYS_CONFIG,
+                               build_message(&strbuf, BACKEND_AUDITD, fields),
+                               NULL,
+                               NULL,
+                               NULL,
+                               success);
     }
 #endif
 
     if (nm_logging_enabled(AUDIT_LOG_LEVEL, LOGD_AUDIT)) {
-        msg = build_message(fields, BACKEND_LOG);
-        _NMLOG(AUDIT_LOG_LEVEL, LOGD_AUDIT, "%s", msg);
-        g_free(msg);
+        _nm_log_full(file,
+                     line,
+                     func,
+                     !(NM_THREAD_SAFE_ON_MAIN_THREAD),
+                     AUDIT_LOG_LEVEL,
+                     LOGD_AUDIT,
+                     0,
+                     NULL,
+                     NULL,
+                     "%s%s",
+                     _NMLOG_PREFIX_NAME ": ",
+                     build_message(&strbuf, BACKEND_LOG, fields));
     }
 }
 
@@ -190,9 +219,13 @@ _audit_log_helper(NMAuditManager *self,
                   gpointer        subject_context,
                   const char *    reason)
 {
-    AuditField      op_field = {}, pid_field = {}, uid_field = {};
-    AuditField      result_field = {}, reason_field = {};
-    gulong          pid, uid;
+    AuditField      op_field;
+    AuditField      pid_field;
+    AuditField      uid_field;
+    AuditField      result_field;
+    AuditField      reason_field;
+    gulong          pid;
+    gulong          uid;
     NMAuthSubject * subject                     = NULL;
     gs_unref_object NMAuthSubject *subject_free = NULL;
 
@@ -213,11 +246,11 @@ _audit_log_helper(NMAuditManager *self,
         pid = nm_auth_subject_get_unix_process_pid(subject);
         uid = nm_auth_subject_get_unix_process_uid(subject);
         if (pid != G_MAXULONG) {
-            _audit_field_init_uint(&pid_field, "pid", pid, BACKEND_ALL);
+            _audit_field_init_uint64(&pid_field, "pid", pid, BACKEND_ALL);
             g_ptr_array_add(fields, &pid_field);
         }
         if (uid != G_MAXULONG) {
-            _audit_field_init_uint(&uid_field, "uid", uid, BACKEND_ALL);
+            _audit_field_init_uint64(&uid_field, "uid", uid, BACKEND_ALL);
             g_ptr_array_add(fields, &uid_field);
         }
     }
@@ -262,8 +295,10 @@ _nm_audit_manager_log_connection_op(NMAuditManager *      self,
                                     gpointer              subject_context,
                                     const char *          reason)
 {
-    gs_unref_ptrarray GPtrArray *fields     = NULL;
-    AuditField                   uuid_field = {}, name_field = {}, args_field = {};
+    gs_unref_ptrarray GPtrArray *fields = NULL;
+    AuditField                   uuid_field;
+    AuditField                   name_field;
+    AuditField                   args_field;
 
     g_return_if_fail(op);
 
@@ -304,8 +339,8 @@ _nm_audit_manager_log_generic_op(NMAuditManager *self,
                                  gpointer        subject_context,
                                  const char *    reason)
 {
-    gs_unref_ptrarray GPtrArray *fields    = NULL;
-    AuditField                   arg_field = {};
+    gs_unref_ptrarray GPtrArray *fields = NULL;
+    AuditField                   arg_field;
 
     g_return_if_fail(op);
     g_return_if_fail(arg);
@@ -330,8 +365,10 @@ _nm_audit_manager_log_device_op(NMAuditManager *self,
                                 gpointer        subject_context,
                                 const char *    reason)
 {
-    gs_unref_ptrarray GPtrArray *fields          = NULL;
-    AuditField                   interface_field = {}, ifindex_field = {}, args_field = {};
+    gs_unref_ptrarray GPtrArray *fields = NULL;
+    AuditField                   interface_field;
+    AuditField                   ifindex_field;
+    AuditField                   args_field;
     int                          ifindex;
 
     g_return_if_fail(op);
@@ -348,7 +385,7 @@ _nm_audit_manager_log_device_op(NMAuditManager *self,
 
     ifindex = nm_device_get_ip_ifindex(device);
     if (ifindex > 0) {
-        _audit_field_init_uint(&ifindex_field, "ifindex", ifindex, BACKEND_ALL);
+        _audit_field_init_uint64(&ifindex_field, "ifindex", ifindex, BACKEND_ALL);
         g_ptr_array_add(fields, &ifindex_field);
     }
 
diff --git a/src/core/nm-auth-manager.c b/src/core/nm-auth-manager.c
index 9470838c..5c0e465a 100644
--- a/src/core/nm-auth-manager.c
+++ b/src/core/nm-auth-manager.c
@@ -8,9 +8,9 @@
 #include "nm-auth-manager.h"
 
 #include "c-list/src/c-list.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
 #include "nm-errors.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
 #include "NetworkManagerUtils.h"
 
diff --git a/src/core/nm-auth-manager.h b/src/core/nm-auth-manager.h
index 991083c1..0586322f 100644
--- a/src/core/nm-auth-manager.h
+++ b/src/core/nm-auth-manager.h
@@ -6,7 +6,7 @@
 #ifndef NM_AUTH_MANAGER_H
 #define NM_AUTH_MANAGER_H
 
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-config-data.h"
 
 /*****************************************************************************/
diff --git a/src/core/nm-auth-utils.c b/src/core/nm-auth-utils.c
index 12da5749..006264dd 100644
--- a/src/core/nm-auth-utils.c
+++ b/src/core/nm-auth-utils.c
@@ -7,9 +7,9 @@
 
 #include "nm-auth-utils.h"
 
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-setting-connection.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-auth-manager.h"
 #include "nm-session-monitor.h"
 #include "nm-dbus-manager.h"
diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c
index 2088c247..164f4a86 100644
--- a/src/core/nm-checkpoint.c
+++ b/src/core/nm-checkpoint.c
@@ -9,7 +9,7 @@
 
 #include "nm-active-connection.h"
 #include "nm-act-request.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-core-utils.h"
 #include "nm-dbus-interface.h"
 #include "devices/nm-device.h"
@@ -229,14 +229,16 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
         if (need_update) {
             _LOGD("rollback: updating connection %s", nm_settings_connection_get_uuid(connection));
             persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
-            nm_settings_connection_update(connection,
-                                          dev_checkpoint->settings_connection,
-                                          persist_mode,
-                                          sett_flags,
-                                          sett_mask,
-                                          NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
-                                          "checkpoint-rollback",
-                                          NULL);
+            nm_settings_connection_update(
+                connection,
+                dev_checkpoint->settings_connection,
+                persist_mode,
+                sett_flags,
+                sett_mask,
+                NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+                    | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET,
+                "checkpoint-rollback",
+                NULL);
         }
     } else {
         /* The connection was deleted, recreate it */
@@ -737,16 +739,12 @@ dispose(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_checkpoint = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_CHECKPOINT,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Devices",
-                                                             "ao",
-                                                             NM_CHECKPOINT_DEVICES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Created", "x", NM_CHECKPOINT_CREATED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RollbackTimeout",
-                                                             "u",
-                                                             NM_CHECKPOINT_ROLLBACK_TIMEOUT), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Devices", "ao", NM_CHECKPOINT_DEVICES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Created", "x", NM_CHECKPOINT_CREATED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RollbackTimeout",
+                                                           "u",
+                                                           NM_CHECKPOINT_ROLLBACK_TIMEOUT), ), ),
 };
 
 static void
diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c
index c62c6773..e127ea23 100644
--- a/src/core/nm-config-data.c
+++ b/src/core/nm-config-data.c
@@ -10,9 +10,9 @@
 
 #include "nm-config.h"
 #include "devices/nm-device.h"
-#include "nm-core-internal.h"
-#include "nm-keyfile-internal.h"
-#include "nm-keyfile-utils.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-keyfile-utils.h"
 
 /*****************************************************************************/
 
@@ -26,6 +26,8 @@ typedef struct {
         gboolean has;
         GSList * spec;
     } match_device;
+    gsize                    lookup_len;
+    const NMUtilsNamedValue *lookup_idx;
 } MatchSectionInfo;
 
 struct _NMGlobalDnsDomain {
@@ -97,6 +99,8 @@ typedef struct {
     NMGlobalDnsConfig *global_dns;
 
     bool systemd_resolved : 1;
+
+    char *iwd_config_path;
 } NMConfigDataPrivate;
 
 struct _NMConfigData {
@@ -114,6 +118,11 @@ G_DEFINE_TYPE(NMConfigData, nm_config_data, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+static const char *
+_match_section_info_get_str(const MatchSectionInfo *m, GKeyFile *keyfile, const char *property);
+
+/*****************************************************************************/
+
 const char *
 nm_config_data_get_config_main_file(const NMConfigData *self)
 {
@@ -341,12 +350,18 @@ nm_config_data_get_systemd_resolved(const NMConfigData *self)
     return NM_CONFIG_DATA_GET_PRIVATE(self)->systemd_resolved;
 }
 
+const char *
+nm_config_data_get_iwd_config_path(const NMConfigData *self)
+{
+    return NM_CONFIG_DATA_GET_PRIVATE(self)->iwd_config_path;
+}
+
 gboolean
 nm_config_data_get_ignore_carrier(const NMConfigData *self, NMDevice *device)
 {
-    gs_free char *value = NULL;
-    gboolean      has_match;
-    int           m;
+    const char *value;
+    gboolean    has_match;
+    int         m;
 
     g_return_val_if_fail(NM_IS_CONFIG_DATA(self), FALSE);
     g_return_val_if_fail(NM_IS_DEVICE(device), FALSE);
@@ -437,7 +452,7 @@ static NMAuthPolkitMode
 _config_data_get_main_auth_polkit(const NMConfigData *self, gboolean *out_invalid_config)
 {
     NMAuthPolkitMode auth_polkit_mode;
-    const char *     str;
+    gs_free char *   str = NULL;
 
     str              = nm_config_data_get_value(self,
                                    NM_CONFIG_KEYFILE_GROUP_MAIN,
@@ -576,7 +591,7 @@ _merge_keyfiles(GKeyFile *keyfile_user, GKeyFile *keyfile_intern)
             const char *  key   = keys[k];
             gs_free char *value = NULL;
 
-            if (is_atomic && strcmp(key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0)
+            if (is_atomic && nm_streq(key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS))
                 continue;
 
             if (!is_intern && !is_atomic
@@ -684,6 +699,7 @@ static const struct {
      NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
      NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT},
     {NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_DHCP, NM_CONFIG_DEFAULT_MAIN_DHCP},
+    {NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH, ""},
     {NM_CONFIG_KEYFILE_GROUP_LOGGING, "backend", NM_CONFIG_DEFAULT_LOGGING_BACKEND},
     {NM_CONFIG_KEYFILE_GROUP_LOGGING, "audit", NM_CONFIG_DEFAULT_LOGGING_AUDIT},
 };
@@ -1116,7 +1132,7 @@ load_global_dns(GKeyFile *keyfile, gboolean internal)
 
         g_hash_table_insert(dns_config->domains, strdup(name), domain);
 
-        if (!strcmp(name, "*"))
+        if (name[0] == '*' && name[1] == '\0')
             default_found = TRUE;
     }
 
@@ -1213,7 +1229,7 @@ global_dns_domain_from_dbus(char *name, GVariant *variant)
 
     g_variant_iter_init(&iter, variant);
     while (g_variant_iter_next(&iter, "{&sv}", &key, &val)) {
-        if (!g_strcmp0(key, "servers") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
+        if (nm_streq0(key, "servers") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
             strv = g_variant_dup_strv(val, NULL);
             _nm_utils_strv_cleanup(strv, TRUE, TRUE, TRUE);
             for (i = 0, j = 0; strv && strv[i]; i++) {
@@ -1230,7 +1246,7 @@ global_dns_domain_from_dbus(char *name, GVariant *variant)
                 g_strfreev(domain->servers);
                 domain->servers = strv;
             }
-        } else if (!g_strcmp0(key, "options") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
+        } else if (nm_streq0(key, "options") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
             strv = g_variant_dup_strv(val, NULL);
             g_strfreev(domain->options);
             domain->options = _nm_utils_strv_cleanup(strv, TRUE, TRUE, TRUE);
@@ -1278,10 +1294,10 @@ nm_global_dns_config_from_dbus(const GValue *value, GError **error)
 
     g_variant_iter_init(&iter, variant);
     while (g_variant_iter_next(&iter, "{&sv}", &key, &val)) {
-        if (!g_strcmp0(key, "searches") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
+        if (nm_streq0(key, "searches") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
             strv                 = g_variant_dup_strv(val, NULL);
             dns_config->searches = _nm_utils_strv_cleanup(strv, TRUE, TRUE, TRUE);
-        } else if (!g_strcmp0(key, "options") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
+        } else if (nm_streq0(key, "options") && g_variant_is_of_type(val, G_VARIANT_TYPE("as"))) {
             strv = g_variant_dup_strv(val, NULL);
             _nm_utils_strv_cleanup(strv, TRUE, TRUE, TRUE);
 
@@ -1297,7 +1313,7 @@ nm_global_dns_config_from_dbus(const GValue *value, GError **error)
                 strv[j]             = NULL;
                 dns_config->options = strv;
             }
-        } else if (!g_strcmp0(key, "domains")
+        } else if (nm_streq0(key, "domains")
                    && g_variant_is_of_type(val, G_VARIANT_TYPE("a{sv}"))) {
             NMGlobalDnsDomain *domain;
             GVariantIter       domain_iter;
@@ -1381,18 +1397,18 @@ _match_section_infos_lookup(const MatchSectionInfo *match_section_infos,
                             NMDevice *              device,
                             const NMPlatformLink *  pllink,
                             const char *            match_device_type,
-                            char **                 out_value)
+                            const char **           out_value)
 {
     const char *match_dhcp_plugin;
 
     if (!match_section_infos)
-        return NULL;
+        goto out;
 
     match_dhcp_plugin = nm_dhcp_manager_get_config(nm_dhcp_manager_get());
 
     for (; match_section_infos->group_name; match_section_infos++) {
-        char *   value = NULL;
-        gboolean match;
+        const char *value;
+        gboolean    match;
 
         /* FIXME: Here we use g_key_file_get_string(). This should be in sync with what keyfile-reader
          * does.
@@ -1401,7 +1417,7 @@ _match_section_infos_lookup(const MatchSectionInfo *match_section_infos,
          * string_to_value(keyfile_to_string(keyfile)) in one. Optimally, keyfile library would
          * expose both functions, and we would return here keyfile_to_string(keyfile).
          * The caller then could convert the string to the proper value via string_to_value(value). */
-        value = g_key_file_get_string(keyfile, match_section_infos->group_name, property, NULL);
+        value = _match_section_info_get_str(match_section_infos, keyfile, property);
         if (!value && !match_section_infos->stop_match)
             continue;
 
@@ -1423,12 +1439,14 @@ _match_section_infos_lookup(const MatchSectionInfo *match_section_infos,
             *out_value = value;
             return match_section_infos;
         }
-        g_free(value);
     }
+
+out:
+    *out_value = NULL;
     return NULL;
 }
 
-char *
+const char *
 nm_config_data_get_device_config(const NMConfigData *self,
                                  const char *        property,
                                  NMDevice *          device,
@@ -1436,7 +1454,7 @@ nm_config_data_get_device_config(const NMConfigData *self,
 {
     const NMConfigDataPrivate *priv;
     const MatchSectionInfo *   connection_info;
-    char *                     value = NULL;
+    const char *               value;
 
     NM_SET_OUT(has_match, FALSE);
 
@@ -1456,7 +1474,7 @@ nm_config_data_get_device_config(const NMConfigData *self,
     return value;
 }
 
-char *
+const char *
 nm_config_data_get_device_config_by_pllink(const NMConfigData *  self,
                                            const char *          property,
                                            const NMPlatformLink *pllink,
@@ -1465,7 +1483,7 @@ nm_config_data_get_device_config_by_pllink(const NMConfigData *  self,
 {
     const NMConfigDataPrivate *priv;
     const MatchSectionInfo *   connection_info;
-    char *                     value = NULL;
+    const char *               value;
 
     g_return_val_if_fail(self, NULL);
     g_return_val_if_fail(property && *property, NULL);
@@ -1490,8 +1508,8 @@ nm_config_data_get_device_config_boolean(const NMConfigData *self,
                                          int                 val_no_match,
                                          int                 val_invalid)
 {
-    gs_free char *value = NULL;
-    gboolean      has_match;
+    const char *value;
+    gboolean    has_match;
 
     value = nm_config_data_get_device_config(self, property, device, &has_match);
     if (!has_match)
@@ -1499,13 +1517,34 @@ nm_config_data_get_device_config_boolean(const NMConfigData *self,
     return nm_config_parse_boolean(value, val_invalid);
 }
 
-char *
+gint64
+nm_config_data_get_device_config_int64(const NMConfigData *self,
+                                       const char *        property,
+                                       NMDevice *          device,
+                                       int                 base,
+                                       gint64              min,
+                                       gint64              max,
+                                       gint64              val_no_match,
+                                       gint64              val_invalid)
+{
+    const char *value;
+    gboolean    has_match;
+
+    value = nm_config_data_get_device_config(self, property, device, &has_match);
+    if (!has_match) {
+        errno = ENOENT;
+        return val_no_match;
+    }
+    return _nm_utils_ascii_str_to_int64(value, base, min, max, val_invalid);
+}
+
+const char *
 nm_config_data_get_connection_default(const NMConfigData *self,
                                       const char *        property,
                                       NMDevice *          device)
 {
     const NMConfigDataPrivate *priv;
-    char *                     value = NULL;
+    const char *               value;
 
     g_return_val_if_fail(self, NULL);
     g_return_val_if_fail(property && *property, NULL);
@@ -1544,15 +1583,41 @@ nm_config_data_get_connection_default_int64(const NMConfigData *self,
                                             gint64              max,
                                             gint64              fallback)
 {
-    gs_free char *value = NULL;
+    const char *value;
 
     value = nm_config_data_get_connection_default(self, property, device);
     return _nm_utils_ascii_str_to_int64(value, 10, min, max, fallback);
 }
 
+static const char *
+_match_section_info_get_str(const MatchSectionInfo *m, GKeyFile *keyfile, const char *property)
+{
+    gssize      idx;
+    const char *value;
+
+    idx   = nm_utils_named_value_list_find(m->lookup_idx, m->lookup_len, property, TRUE);
+    value = idx >= 0 ? m->lookup_idx[idx].value_str : NULL;
+
+#if NM_MORE_ASSERTS > 10
+    {
+        gs_free char *value2 = g_key_file_get_string(keyfile, m->group_name, property, NULL);
+
+        nm_assert(nm_streq0(value2, value));
+    }
+#endif
+
+    return value;
+}
+
 static void
-_get_connection_info_init(MatchSectionInfo *connection_info, GKeyFile *keyfile, char *group)
+_match_section_info_init(MatchSectionInfo *connection_info, GKeyFile *keyfile, char *group)
 {
+    char **            keys = NULL;
+    gsize              n_keys;
+    gsize              i;
+    gsize              j;
+    NMUtilsNamedValue *vals;
+
     /* pass ownership of @group on... */
     connection_info->group_name = group;
 
@@ -1563,18 +1628,66 @@ _get_connection_info_init(MatchSectionInfo *connection_info, GKeyFile *keyfile,
                                  &connection_info->match_device.has);
     connection_info->stop_match =
         nm_config_keyfile_get_boolean(keyfile, group, NM_CONFIG_KEYFILE_KEY_STOP_MATCH, FALSE);
+
+    keys = g_key_file_get_keys(keyfile, group, &n_keys, NULL);
+    nm_utils_strv_sort(keys, n_keys);
+
+    vals = g_new(NMUtilsNamedValue, n_keys);
+
+    for (i = 0, j = 0; i < n_keys; i++) {
+        gs_free char *key = g_steal_pointer(&keys[i]);
+        char *        value;
+
+        if (NM_IN_STRSET(key, NM_CONFIG_KEYFILE_KEY_STOP_MATCH, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE))
+            continue;
+
+        if (j > 0 && nm_streq(vals[j - 1].name, key))
+            continue;
+
+        value = g_key_file_get_string(keyfile, group, key, NULL);
+        if (!value)
+            continue;
+
+        vals[j++] = (NMUtilsNamedValue){
+            .name      = g_steal_pointer(&key),
+            .value_str = value,
+        };
+    }
+
+    g_free(keys);
+
+    if (n_keys != j) {
+        gs_free NMUtilsNamedValue *vals2 = vals;
+
+        /* since this buffer will be kept around for a long time,
+         * get rid of the excess allocation. */
+        vals   = nm_memdup(vals2, sizeof(NMUtilsNamedValue) * j);
+        n_keys = j;
+    }
+
+    if (n_keys == 0)
+        nm_clear_g_free(&vals);
+
+    connection_info->lookup_idx = vals;
+    connection_info->lookup_len = n_keys;
 }
 
 static void
 _match_section_infos_free(MatchSectionInfo *match_section_infos)
 {
-    guint i;
+    MatchSectionInfo *m;
+    gsize             i;
 
     if (!match_section_infos)
         return;
-    for (i = 0; match_section_infos[i].group_name; i++) {
-        g_free(match_section_infos[i].group_name);
-        g_slist_free_full(match_section_infos[i].match_device.spec, g_free);
+    for (m = match_section_infos; m->group_name; m++) {
+        g_free(m->group_name);
+        g_slist_free_full(m->match_device.spec, g_free);
+        for (i = 0; i < m->lookup_len; i++) {
+            g_free(m->lookup_idx[i].name_mutable);
+            g_free(m->lookup_idx[i].value_str_mutable);
+        }
+        g_free((gpointer) m->lookup_idx);
     }
     g_free(match_section_infos);
 }
@@ -1619,11 +1732,11 @@ _match_section_infos_construct(GKeyFile *keyfile, const char *prefix)
     match_section_infos = g_new0(MatchSectionInfo, ngroups + 1 + (connection_tag ? 1 : 0));
     for (i = 0; i < ngroups; i++) {
         /* pass ownership of @group on... */
-        _get_connection_info_init(&match_section_infos[i], keyfile, groups[ngroups - i - 1]);
+        _match_section_info_init(&match_section_infos[i], keyfile, groups[ngroups - i - 1]);
     }
     if (connection_tag) {
         /* pass ownership of @connection_tag on... */
-        _get_connection_info_init(&match_section_infos[i], keyfile, connection_tag);
+        _match_section_info_init(&match_section_infos[i], keyfile, connection_tag);
     }
     g_free(groups);
 
@@ -1644,28 +1757,26 @@ nm_config_data_diff(NMConfigData *old_data, NMConfigData *new_data)
     priv_old = NM_CONFIG_DATA_GET_PRIVATE(old_data);
     priv_new = NM_CONFIG_DATA_GET_PRIVATE(new_data);
 
-    if (!_nm_keyfile_equals(priv_old->keyfile_user, priv_new->keyfile_user, TRUE))
+    if (!_nm_keyfile_equal(priv_old->keyfile_user, priv_new->keyfile_user, TRUE))
         changes |= NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER;
 
-    if (!_nm_keyfile_equals(priv_old->keyfile_intern, priv_new->keyfile_intern, TRUE))
+    if (!_nm_keyfile_equal(priv_old->keyfile_intern, priv_new->keyfile_intern, TRUE))
         changes |= NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN;
 
-    if (g_strcmp0(nm_config_data_get_config_main_file(old_data),
-                  nm_config_data_get_config_main_file(new_data))
-            != 0
-        || g_strcmp0(nm_config_data_get_config_description(old_data),
-                     nm_config_data_get_config_description(new_data))
-               != 0)
+    if (!nm_streq0(nm_config_data_get_config_main_file(old_data),
+                   nm_config_data_get_config_main_file(new_data))
+        || !nm_streq0(nm_config_data_get_config_description(old_data),
+                      nm_config_data_get_config_description(new_data)))
         changes |= NM_CONFIG_CHANGE_CONFIG_FILES;
 
     if (nm_config_data_get_connectivity_enabled(old_data)
             != nm_config_data_get_connectivity_enabled(new_data)
         || nm_config_data_get_connectivity_interval(old_data)
                != nm_config_data_get_connectivity_interval(new_data)
-        || g_strcmp0(nm_config_data_get_connectivity_uri(old_data),
-                     nm_config_data_get_connectivity_uri(new_data))
-        || g_strcmp0(nm_config_data_get_connectivity_response(old_data),
-                     nm_config_data_get_connectivity_response(new_data)))
+        || !nm_streq0(nm_config_data_get_connectivity_uri(old_data),
+                      nm_config_data_get_connectivity_uri(new_data))
+        || !nm_streq0(nm_config_data_get_connectivity_response(old_data),
+                      nm_config_data_get_connectivity_response(new_data)))
         changes |= NM_CONFIG_CHANGE_CONNECTIVITY;
 
     if (nm_utils_g_slist_strlist_cmp(priv_old->no_auto_default.specs,
@@ -1676,10 +1787,11 @@ nm_config_data_diff(NMConfigData *old_data, NMConfigData *new_data)
                != 0)
         changes |= NM_CONFIG_CHANGE_NO_AUTO_DEFAULT;
 
-    if (g_strcmp0(nm_config_data_get_dns_mode(old_data), nm_config_data_get_dns_mode(new_data)))
+    if (!nm_streq0(nm_config_data_get_dns_mode(old_data), nm_config_data_get_dns_mode(new_data)))
         changes |= NM_CONFIG_CHANGE_DNS_MODE;
 
-    if (g_strcmp0(nm_config_data_get_rc_manager(old_data), nm_config_data_get_rc_manager(new_data)))
+    if (!nm_streq0(nm_config_data_get_rc_manager(old_data),
+                   nm_config_data_get_rc_manager(new_data)))
         changes |= NM_CONFIG_CHANGE_RC_MANAGER;
 
     if (!global_dns_equal(priv_old->global_dns, priv_new->global_dns))
@@ -1910,6 +2022,12 @@ constructed(GObject *object)
     if (!priv->global_dns)
         priv->global_dns = load_global_dns(priv->keyfile_intern, TRUE);
 
+    priv->iwd_config_path =
+        nm_strstrip(g_key_file_get_string(priv->keyfile,
+                                          NM_CONFIG_KEYFILE_GROUP_MAIN,
+                                          NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH,
+                                          NULL));
+
     G_OBJECT_CLASS(nm_config_data_parent_class)->constructed(object);
 }
 
@@ -1996,6 +2114,8 @@ finalize(GObject *gobject)
 
     nm_global_dns_config_free(priv->global_dns);
 
+    g_free(priv->iwd_config_path);
+
     _match_section_infos_free(priv->connection_infos);
     _match_section_infos_free(priv->device_infos);
 
diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h
index 07cffb43..42d69a8b 100644
--- a/src/core/nm-config-data.h
+++ b/src/core/nm-config-data.h
@@ -6,6 +6,8 @@
 #ifndef NM_CONFIG_DATA_H
 #define NM_CONFIG_DATA_H
 
+#include "libnm-platform/nmp-base.h"
+
 /*****************************************************************************/
 
 typedef enum {
@@ -48,71 +50,71 @@ typedef enum {
 #define NM_CONFIG_DATA_NO_AUTO_DEFAULT       "no-auto-default"
 #define NM_CONFIG_DATA_DNS_MODE              "dns"
 
-typedef enum { /*< flags >*/
-               NM_CONFIG_GET_VALUE_NONE = 0,
+typedef enum {
+    NM_CONFIG_GET_VALUE_NONE = 0,
 
-               /* use g_key_file_get_value() instead of g_key_file_get_string(). */
-               NM_CONFIG_GET_VALUE_RAW = (1LL << 0),
+    /* use g_key_file_get_value() instead of g_key_file_get_string(). */
+    NM_CONFIG_GET_VALUE_RAW = (1LL << 0),
 
-               /* strip whitespaces */
-               NM_CONFIG_GET_VALUE_STRIP = (1LL << 1),
+    /* strip whitespaces */
+    NM_CONFIG_GET_VALUE_STRIP = (1LL << 1),
 
-               /* if the returned string would be the empty word, return NULL. */
-               NM_CONFIG_GET_VALUE_NO_EMPTY = (1LL << 2),
+    /* if the returned string would be the empty word, return NULL. */
+    NM_CONFIG_GET_VALUE_NO_EMPTY = (1LL << 2),
 
-               /* special flag to read device spec. You want to use this before passing the
+    /* special flag to read device spec. You want to use this before passing the
      * value to nm_match_spec_split(). */
-               NM_CONFIG_GET_VALUE_TYPE_SPEC = NM_CONFIG_GET_VALUE_RAW,
+    NM_CONFIG_GET_VALUE_TYPE_SPEC = NM_CONFIG_GET_VALUE_RAW,
 } NMConfigGetValueFlags;
 
-typedef enum { /*< flags >*/
-               NM_CONFIG_CHANGE_NONE = 0,
+typedef enum {
+    NM_CONFIG_CHANGE_NONE = 0,
 
-               /**************************************************************************
+    /**************************************************************************
      * The external cause which triggered the reload/configuration-change
      *************************************************************************/
 
-               NM_CONFIG_CHANGE_CAUSE_SIGHUP          = (1L << 0),
-               NM_CONFIG_CHANGE_CAUSE_SIGUSR1         = (1L << 1),
-               NM_CONFIG_CHANGE_CAUSE_SIGUSR2         = (1L << 2),
-               NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT = (1L << 3),
-               NM_CONFIG_CHANGE_CAUSE_SET_VALUES      = (1L << 4),
-               NM_CONFIG_CHANGE_CAUSE_CONF            = (1L << 5),
-               NM_CONFIG_CHANGE_CAUSE_DNS_RC          = (1L << 6),
-               NM_CONFIG_CHANGE_CAUSE_DNS_FULL        = (1L << 7),
+    NM_CONFIG_CHANGE_CAUSE_SIGHUP          = (1L << 0),
+    NM_CONFIG_CHANGE_CAUSE_SIGUSR1         = (1L << 1),
+    NM_CONFIG_CHANGE_CAUSE_SIGUSR2         = (1L << 2),
+    NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT = (1L << 3),
+    NM_CONFIG_CHANGE_CAUSE_SET_VALUES      = (1L << 4),
+    NM_CONFIG_CHANGE_CAUSE_CONF            = (1L << 5),
+    NM_CONFIG_CHANGE_CAUSE_DNS_RC          = (1L << 6),
+    NM_CONFIG_CHANGE_CAUSE_DNS_FULL        = (1L << 7),
 
-               NM_CONFIG_CHANGE_CAUSES = ((1L << 8) - 1),
+    NM_CONFIG_CHANGE_CAUSES = ((1L << 8) - 1),
 
-               /**************************************************************************
+    /**************************************************************************
      * Following flags describe which property of the configuration changed:
      *************************************************************************/
 
-               /* main-file or config-description changed */
-               NM_CONFIG_CHANGE_CONFIG_FILES = (1L << 10),
+    /* main-file or config-description changed */
+    NM_CONFIG_CHANGE_CONFIG_FILES = (1L << 10),
 
-               /* any configuration on disk changed */
-               NM_CONFIG_CHANGE_VALUES = (1L << 11),
+    /* any configuration on disk changed */
+    NM_CONFIG_CHANGE_VALUES = (1L << 11),
 
-               /* any user configuration on disk changed (NetworkManager.conf) */
-               NM_CONFIG_CHANGE_VALUES_USER = (1L << 12),
+    /* any user configuration on disk changed (NetworkManager.conf) */
+    NM_CONFIG_CHANGE_VALUES_USER = (1L << 12),
 
-               /* any internal configuration on disk changed (NetworkManager-intern.conf) */
-               NM_CONFIG_CHANGE_VALUES_INTERN = (1L << 13),
+    /* any internal configuration on disk changed (NetworkManager-intern.conf) */
+    NM_CONFIG_CHANGE_VALUES_INTERN = (1L << 13),
 
-               /* configuration regarding connectivity changed */
-               NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 14),
+    /* configuration regarding connectivity changed */
+    NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 14),
 
-               /* configuration regarding no-auto-default changed */
-               NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 15),
+    /* configuration regarding no-auto-default changed */
+    NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 15),
 
-               /* configuration regarding dns-mode changed */
-               NM_CONFIG_CHANGE_DNS_MODE = (1L << 16),
+    /* configuration regarding dns-mode changed */
+    NM_CONFIG_CHANGE_DNS_MODE = (1L << 16),
 
-               /* configuration regarding rc-manager changed */
-               NM_CONFIG_CHANGE_RC_MANAGER = (1L << 17),
+    /* configuration regarding rc-manager changed */
+    NM_CONFIG_CHANGE_RC_MANAGER = (1L << 17),
 
-               /* configuration regarding global dns-config changed */
-               NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG = (1L << 18),
+    /* configuration regarding global dns-config changed */
+    NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG = (1L << 18),
 
 } NMConfigChangeFlags;
 
@@ -188,6 +190,8 @@ int      nm_config_data_get_sriov_num_vfs(const NMConfigData *self, NMDevice *de
 
 NMGlobalDnsConfig *nm_config_data_get_global_dns_config(const NMConfigData *self);
 
+const char *nm_config_data_get_iwd_config_path(const NMConfigData *self);
+
 extern const char *__start_connection_defaults[];
 extern const char *__stop_connection_defaults[];
 
@@ -203,9 +207,9 @@ extern const char *__stop_connection_defaults[];
         name;                                                                               \
     })
 
-char *nm_config_data_get_connection_default(const NMConfigData *self,
-                                            const char *        property,
-                                            NMDevice *          device);
+const char *nm_config_data_get_connection_default(const NMConfigData *self,
+                                                  const char *        property,
+                                                  NMDevice *          device);
 
 gint64 nm_config_data_get_connection_default_int64(const NMConfigData *self,
                                                    const char *        property,
@@ -214,22 +218,30 @@ gint64 nm_config_data_get_connection_default_int64(const NMConfigData *self,
                                                    gint64              max,
                                                    gint64              fallback);
 
-char *nm_config_data_get_device_config(const NMConfigData *self,
-                                       const char *        property,
-                                       NMDevice *          device,
-                                       gboolean *          has_match);
+const char *nm_config_data_get_device_config(const NMConfigData *self,
+                                             const char *        property,
+                                             NMDevice *          device,
+                                             gboolean *          has_match);
 
-char *nm_config_data_get_device_config_by_pllink(const NMConfigData *  self,
-                                                 const char *          property,
-                                                 const NMPlatformLink *pllink,
-                                                 const char *          match_device_type,
-                                                 gboolean *            has_match);
+const char *nm_config_data_get_device_config_by_pllink(const NMConfigData *  self,
+                                                       const char *          property,
+                                                       const NMPlatformLink *pllink,
+                                                       const char *          match_device_type,
+                                                       gboolean *            has_match);
 
 gboolean nm_config_data_get_device_config_boolean(const NMConfigData *self,
                                                   const char *        property,
                                                   NMDevice *          device,
                                                   int                 val_no_match,
                                                   int                 val_invalid);
+gint64   nm_config_data_get_device_config_int64(const NMConfigData *self,
+                                                const char *        property,
+                                                NMDevice *          device,
+                                                int                 base,
+                                                gint64              min,
+                                                gint64              max,
+                                                gint64              val_no_match,
+                                                gint64              val_invalid);
 
 char **  nm_config_data_get_groups(const NMConfigData *self);
 char **  nm_config_data_get_keys(const NMConfigData *self, const char *group);
diff --git a/src/core/nm-config.c b/src/core/nm-config.c
index 55323919..fdc450a0 100644
--- a/src/core/nm-config.c
+++ b/src/core/nm-config.c
@@ -13,9 +13,9 @@
 #include "nm-utils.h"
 #include "devices/nm-device.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
-#include "nm-keyfile-internal.h"
-#include "nm-keyfile-utils.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-keyfile-utils.h"
 
 #define DEFAULT_CONFIG_MAIN_FILE     NMCONFDIR "/NetworkManager.conf"
 #define DEFAULT_CONFIG_DIR           NMCONFDIR "/conf.d"
@@ -433,13 +433,7 @@ nm_config_set_no_auto_default_for_device(NMConfig *self, NMDevice *device)
 
     len = NM_PTRARRAY_LEN(no_auto_default_current);
 
-    idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) no_auto_default_current,
-                                               len,
-                                               spec,
-                                               nm_strcmp_with_data,
-                                               NULL,
-                                               NULL,
-                                               NULL);
+    idx = nm_utils_strv_find_binary_search(no_auto_default_current, len, spec);
     if (idx >= 0) {
         /* @spec is already blocked. We don't have to update our in-memory representation.
          * Maybe we should write to no_auto_default_file anew, but let's save that too. */
@@ -842,8 +836,10 @@ static const ConfigGroup config_groups[] = {
                              NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG,
                              NM_CONFIG_KEYFILE_KEY_MAIN_DHCP,
                              NM_CONFIG_KEYFILE_KEY_MAIN_DNS,
+                             NM_CONFIG_KEYFILE_KEY_MAIN_FIREWALL_BACKEND,
                              NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE,
                              NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER,
+                             NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH,
                              NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES,
                              NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT,
                              NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS,
@@ -2115,7 +2111,7 @@ nm_config_set_values(NMConfig *self,
                                  "");
     }
 
-    if (!_nm_keyfile_equals(keyfile_intern_current, keyfile_new, TRUE))
+    if (!_nm_keyfile_equal(keyfile_intern_current, keyfile_new, TRUE))
         new_data = nm_config_data_new_update_keyfile_intern(priv->config_data, keyfile_new);
 
     _LOGD("set values(): %s", new_data ? "has changes" : "no changes");
@@ -3149,7 +3145,7 @@ nm_config_class_init(NMConfigClass *config_class)
                      NM_TYPE_CONFIG_DATA);
 
     G_STATIC_ASSERT_EXPR(sizeof(guint) == sizeof(NMConfigChangeFlags));
-    G_STATIC_ASSERT_EXPR(((gint64)((NMConfigChangeFlags) -1)) > ((gint64) 0));
+    G_STATIC_ASSERT_EXPR(((gint64) ((NMConfigChangeFlags) -1)) > ((gint64) 0));
 }
 
 static void
diff --git a/src/core/nm-config.h b/src/core/nm-config.h
index 6f3f514c..7f1c0c4c 100644
--- a/src/core/nm-config.h
+++ b/src/core/nm-config.h
@@ -9,6 +9,8 @@
 
 #include "nm-config-data.h"
 
+#include "libnm-base/nm-config-base.h"
+
 #define NM_TYPE_CONFIG            (nm_config_get_type())
 #define NM_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CONFIG, NMConfig))
 #define NM_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_CONFIG, NMConfigClass))
@@ -26,84 +28,6 @@
 #define NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL 300
 #define NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE "NetworkManager is online" /* NOT LOCALIZED */
 
-#define NM_CONFIG_KEYFILE_LIST_SEPARATOR ','
-
-#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN                 ".intern."
-#define NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION             "connection"
-#define NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE                 "device"
-#define NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN      "global-dns-domain-"
-#define NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".test-append-stringlist"
-
-#define NM_CONFIG_KEYFILE_GROUP_MAIN         "main"
-#define NM_CONFIG_KEYFILE_GROUP_LOGGING      "logging"
-#define NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY "connectivity"
-#define NM_CONFIG_KEYFILE_GROUP_KEYFILE      "keyfile"
-#define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN     "ifupdown"
-#define NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS   "global-dns"
-#define NM_CONFIG_KEYFILE_GROUP_CONFIG       ".config"
-
-#define NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY          "assume-ipv6ll-only"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT                 "auth-polkit"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT "autoconnect-retries-default"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT          "configure-and-quit"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG                       "debug"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_DHCP                        "dhcp"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_DNS                         "dns"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE               "hostname-mode"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER              "ignore-carrier"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES    "monitor-connection-files"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT             "no-auto-default"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS                     "plugins"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER                  "rc-manager"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER                "slaves-order"
-#define NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED            "systemd-resolved"
-
-#define NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT   "audit"
-#define NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND "backend"
-#define NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS "domains"
-#define NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL   "level"
-
-#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_ENABLED  "enabled"
-#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_INTERVAL "interval"
-#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_RESPONSE "response"
-#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_URI      "uri"
-
-#define NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH              "path"
-#define NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES "unmanaged-devices"
-#define NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME          "hostname"
-
-#define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed"
-
-#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES "searches"
-#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS  "options"
-
-#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS "servers"
-#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS "options"
-
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED                    "managed"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER             "ignore-carrier"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS              "sriov-num-vfs"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND               "wifi.backend"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS "wifi.scan-rand-mac-address"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_GENERATE_MAC_ADDRESS_MASK \
-    "wifi.scan-generate-mac-address-mask"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT "carrier-wait-timeout"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_IWD_AUTOCONNECT "wifi.iwd.autoconnect"
-
-#define NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE "match-device"
-#define NM_CONFIG_KEYFILE_KEY_STOP_MATCH   "stop-match"
-
-#define NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS ".was"   /* check-config-options skip */
-#define NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE      "enable" /* check-config-options skip */
-
-#define NM_CONFIG_KEYFILE_KEYPREFIX_WAS ".was."
-#define NM_CONFIG_KEYFILE_KEYPREFIX_SET ".set."
-
-#define NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS \
-    NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS
-#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN \
-    NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN
-
 typedef struct NMConfigCmdLineOptions NMConfigCmdLineOptions;
 
 typedef enum {
diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c
index f847c8f4..85d41b49 100644
--- a/src/core/nm-connectivity.c
+++ b/src/core/nm-connectivity.c
@@ -16,7 +16,7 @@
 #include <glib-unix.h>
 
 #include "c-list/src/c-list.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
 #include "nm-dbus-manager.h"
@@ -572,7 +572,7 @@ easy_write_cb(void *buffer, size_t size, size_t nmemb, void *userdata)
          * Continue receiving... */
         cb_data->concheck.response_good_cnt += len;
 
-        if (cb_data->concheck.response_good_cnt > (gsize)(100 * 1024)) {
+        if (cb_data->concheck.response_good_cnt > (gsize) (100 * 1024)) {
             /* we expect an empty response. We accept either
              * 1) status code 204 and any response
              * 2) status code 200 and an empty response.
@@ -764,7 +764,7 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data)
 }
 #endif
 
-#define SD_RESOLVED_DNS ((guint64)(1LL << 0))
+#define SD_RESOLVED_DNS ((guint64) (1LL << 0))
 
 static NMConnectivityState
 check_platform_config(NMConnectivity *self,
@@ -904,7 +904,7 @@ nm_connectivity_check_start(NMConnectivity *            self,
          * This is relatively cumbersome to avoid, because we would have to go through
          * NMDnsSystemdResolved trying to asynchronously start the service, to ensure there
          * is only one attempt to start the service. */
-        has_systemd_resolved = nm_dns_manager_has_systemd_resolved(nm_dns_manager_get());
+        has_systemd_resolved = !!nm_dns_manager_get_systemd_resolved(nm_dns_manager_get());
 
         if (has_systemd_resolved) {
             GDBusConnection *dbus_connection;
diff --git a/src/core/nm-connectivity.h b/src/core/nm-connectivity.h
index c67668ce..0985df97 100644
--- a/src/core/nm-connectivity.h
+++ b/src/core/nm-connectivity.h
@@ -8,6 +8,7 @@
 #define __NETWORKMANAGER_CONNECTIVITY_H__
 
 #include "nm-dbus-interface.h"
+#include "libnm-platform/nmp-base.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c
index 9075c30d..41d22565 100644
--- a/src/core/nm-core-utils.c
+++ b/src/core/nm-core-utils.c
@@ -22,13 +22,16 @@
 #include <net/if_arp.h>
 #include <net/ethernet.h>
 
-#include "nm-std-aux/unaligned.h"
-#include "nm-glib-aux/nm-random-utils.h"
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-glib-aux/nm-secret-utils.h"
-#include "nm-glib-aux/nm-time-utils.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-platform/nmp-base.h"
+#include "libnm-std-aux/unaligned.h"
+#include "libnm-glib-aux/nm-random-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "libnm-glib-aux/nm-secret-utils.h"
+#include "libnm-glib-aux/nm-time-utils.h"
+#include "libnm-glib-aux/nm-str-buf.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
@@ -156,37 +159,6 @@ _nm_singleton_instance_register_destruction(GObject *instance)
 
 /*****************************************************************************/
 
-static double
-_exp10(guint16 ex)
-{
-    double v;
-
-    if (ex == 0)
-        return 1.0;
-
-    v = _exp10(ex / 2);
-    v = v * v;
-    if (ex % 2)
-        v *= 10;
-    return v;
-}
-
-/*
- * nm_utils_exp10:
- * @ex: the exponent
- *
- * Returns: 10^ex, or pow(10, ex), or exp10(ex).
- */
-double
-nm_utils_exp10(gint16 ex)
-{
-    if (ex >= 0)
-        return _exp10(ex);
-    return 1.0 / _exp10(-((gint32) ex));
-}
-
-/*****************************************************************************/
-
 gboolean
 nm_ether_addr_is_valid(const NMEtherAddr *addr)
 {
@@ -304,85 +276,6 @@ nm_utils_array_remove_at_indexes(GArray *array, const guint *indexes_to_delete,
     g_array_set_size(array, res_length);
 }
 
-static const char *
-_trunk_first_line(char *str)
-{
-    char *s;
-
-    s = strchr(str, '\n');
-    if (s)
-        s[0] = '\0';
-    return str;
-}
-
-int
-nm_utils_modprobe(GError **error, gboolean suppress_error_logging, const char *arg1, ...)
-{
-    gs_unref_ptrarray GPtrArray *argv = NULL;
-    int                          exit_status;
-    gs_free char *               _log_str = NULL;
-#define ARGV_TO_STR(argv) \
-    (_log_str ? _log_str : (_log_str = g_strjoinv(" ", (char **) argv->pdata)))
-    GError *      local = NULL;
-    va_list       ap;
-    NMLogLevel    llevel  = suppress_error_logging ? LOGL_DEBUG : LOGL_ERR;
-    gs_free char *std_out = NULL, *std_err = NULL;
-
-    g_return_val_if_fail(!error || !*error, -1);
-    g_return_val_if_fail(arg1, -1);
-
-    /* construct the argument list */
-    argv = g_ptr_array_sized_new(4);
-    g_ptr_array_add(argv, "/sbin/modprobe");
-    g_ptr_array_add(argv, "--use-blacklist");
-    g_ptr_array_add(argv, (char *) arg1);
-
-    va_start(ap, arg1);
-    while ((arg1 = va_arg(ap, const char *)))
-        g_ptr_array_add(argv, (char *) arg1);
-    va_end(ap);
-
-    g_ptr_array_add(argv, NULL);
-
-    nm_log_dbg(LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR(argv));
-    if (!g_spawn_sync(NULL,
-                      (char **) argv->pdata,
-                      NULL,
-                      0,
-                      NULL,
-                      NULL,
-                      &std_out,
-                      &std_err,
-                      &exit_status,
-                      &local)) {
-        nm_log(llevel,
-               LOGD_CORE,
-               NULL,
-               NULL,
-               "modprobe: '%s' failed: %s",
-               ARGV_TO_STR(argv),
-               local->message);
-        g_propagate_error(error, local);
-        return -1;
-    } else if (exit_status != 0) {
-        nm_log(llevel,
-               LOGD_CORE,
-               NULL,
-               NULL,
-               "modprobe: '%s' exited with error %d%s%s%s%s%s%s",
-               ARGV_TO_STR(argv),
-               exit_status,
-               std_out && *std_out ? " (" : "",
-               std_out && *std_out ? _trunk_first_line(std_out) : "",
-               std_out && *std_out ? ")" : "",
-               std_err && *std_err ? " (" : "",
-               std_err && *std_err ? _trunk_first_line(std_err) : "",
-               std_err && *std_err ? ")" : "");
-    }
-
-    return exit_status;
-}
-
 /*****************************************************************************/
 
 typedef struct {
@@ -693,9 +586,10 @@ static gulong
 _sleep_duration_convert_ms_to_us(guint32 sleep_duration_msec)
 {
     if (sleep_duration_msec > 0) {
-        guint64 x = (gint64) sleep_duration_msec * (guint64) 1000L;
+        guint64 x = ((guint64) sleep_duration_msec) * 1000UL;
 
-        return x < G_MAXULONG ? (gulong) x : G_MAXULONG;
+        nm_assert(x < G_MAXULONG);
+        return x;
     }
     return G_USEC_PER_SEC / 20;
 }
@@ -1269,7 +1163,7 @@ typedef struct {
         const char *value;
         gboolean    is_parsed;
         guint       len;
-        guint8      bin[NM_UTILS_HWADDR_LEN_MAX];
+        guint8      bin[_NM_UTILS_HWADDR_LEN_MAX];
     } hwaddr;
     struct {
         const char *value;
@@ -1956,7 +1850,8 @@ nm_wildcard_match_check(const char *str, const char *const *patterns, guint num_
 
         _pattern_parse(patterns[i], &p, &is_inverted, &is_mandatory);
 
-        match = (fnmatch(p, str, 0) == 0);
+        match = (fnmatch(p, str ?: "", 0) == 0);
+
         if (is_inverted)
             match = !match;
 
@@ -2051,65 +1946,6 @@ nm_utils_kernel_cmdline_match_check(const char *const *proc_cmdline,
 
 /*****************************************************************************/
 
-char *
-nm_utils_new_vlan_name(const char *parent_iface, guint32 vlan_id)
-{
-    guint id_len;
-    gsize parent_len;
-    char *ifname;
-
-    g_return_val_if_fail(parent_iface && *parent_iface, NULL);
-
-    if (vlan_id < 10)
-        id_len = 2;
-    else if (vlan_id < 100)
-        id_len = 3;
-    else if (vlan_id < 1000)
-        id_len = 4;
-    else {
-        g_return_val_if_fail(vlan_id < 4095, NULL);
-        id_len = 5;
-    }
-
-    ifname = g_new(char, IFNAMSIZ);
-
-    parent_len = strlen(parent_iface);
-    parent_len = MIN(parent_len, IFNAMSIZ - 1 - id_len);
-    memcpy(ifname, parent_iface, parent_len);
-    g_snprintf(&ifname[parent_len], IFNAMSIZ - parent_len, ".%u", vlan_id);
-
-    return ifname;
-}
-
-/* nm_utils_new_infiniband_name:
- * @name: the output-buffer where the value will be written. Must be
- *   not %NULL and point to a string buffer of at least IFNAMSIZ bytes.
- * @parent_name: the parent interface name
- * @p_key: the partition key.
- *
- * Returns: the infiniband name will be written to @name and @name
- *   is returned.
- */
-const char *
-nm_utils_new_infiniband_name(char *name, const char *parent_name, int p_key)
-{
-    g_return_val_if_fail(name, NULL);
-    g_return_val_if_fail(parent_name && parent_name[0], NULL);
-    g_return_val_if_fail(strlen(parent_name) < IFNAMSIZ, NULL);
-
-    /* technically, p_key of 0x0000 and 0x8000 is not allowed either. But we don't
-     * want to assert against that in nm_utils_new_infiniband_name(). So be more
-     * resilient here, and accept those. */
-    g_return_val_if_fail(p_key >= 0 && p_key <= 0xffff, NULL);
-
-    /* If parent+suffix is too long, kernel would just truncate
-     * the name. We do the same. See ipoib_vlan_add().  */
-    g_snprintf(name, IFNAMSIZ, "%s.%04x", parent_name, p_key);
-    return name;
-}
-
-/*****************************************************************************/
-
 /**
  * nm_utils_cmp_connection_by_autoconnect_priority:
  * @a:
@@ -2538,134 +2374,6 @@ out:
     g_array_free(sorted_hashes, TRUE);
 }
 
-#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
-#define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
-G_STATIC_ASSERT(sizeof(IPV4_PROPERTY_DIR) == sizeof(IPV6_PROPERTY_DIR));
-G_STATIC_ASSERT(NM_STRLEN(IPV6_PROPERTY_DIR) + IFNAMSIZ + 60
-                == NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE);
-
-/**
- * nm_utils_sysctl_ip_conf_path:
- * @addr_family: either AF_INET or AF_INET6.
- * @buf: the output buffer where to write the path. It
- *   must be at least NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE bytes
- *   long.
- * @ifname: an interface name
- * @property: a property name
- *
- * Returns: the path to IPv6 property @property on @ifname. Note that
- * this returns the input argument @buf.
- */
-const char *
-nm_utils_sysctl_ip_conf_path(int addr_family, char *buf, const char *ifname, const char *property)
-{
-    int len;
-
-    nm_assert(buf);
-    nm_assert_addr_family(addr_family);
-
-    g_assert(nm_utils_ifname_valid_kernel(ifname, NULL));
-    property = NM_ASSERT_VALID_PATH_COMPONENT(property);
-
-    len = g_snprintf(buf,
-                     NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE,
-                     "%s%s/%s",
-                     addr_family == AF_INET6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR,
-                     ifname,
-                     property);
-    g_assert(len < NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE - 1);
-    return buf;
-}
-
-gboolean
-nm_utils_sysctl_ip_conf_is_path(int         addr_family,
-                                const char *path,
-                                const char *ifname,
-                                const char *property)
-{
-    g_return_val_if_fail(path, FALSE);
-    NM_ASSERT_VALID_PATH_COMPONENT(property);
-    g_assert(!ifname || nm_utils_ifname_valid_kernel(ifname, NULL));
-
-    if (addr_family == AF_INET) {
-        if (!g_str_has_prefix(path, IPV4_PROPERTY_DIR))
-            return FALSE;
-        path += NM_STRLEN(IPV4_PROPERTY_DIR);
-    } else if (addr_family == AF_INET6) {
-        if (!g_str_has_prefix(path, IPV6_PROPERTY_DIR))
-            return FALSE;
-        path += NM_STRLEN(IPV6_PROPERTY_DIR);
-    } else
-        g_return_val_if_reached(FALSE);
-
-    if (ifname) {
-        if (!g_str_has_prefix(path, ifname))
-            return FALSE;
-        path += strlen(ifname);
-        if (path[0] != '/')
-            return FALSE;
-        path++;
-    } else {
-        const char *slash;
-        char        buf[IFNAMSIZ];
-        gsize       l;
-
-        slash = strchr(path, '/');
-        if (!slash)
-            return FALSE;
-        l = slash - path;
-        if (l >= IFNAMSIZ)
-            return FALSE;
-        memcpy(buf, path, l);
-        buf[l] = '\0';
-        if (!nm_utils_ifname_valid_kernel(buf, NULL))
-            return FALSE;
-        path = slash + 1;
-    }
-
-    if (!nm_streq(path, property))
-        return FALSE;
-
-    return TRUE;
-}
-
-gboolean
-nm_utils_is_valid_path_component(const char *name)
-{
-    const char *n;
-
-    if (name == NULL || name[0] == '\0')
-        return FALSE;
-
-    if (name[0] == '.') {
-        if (name[1] == '\0')
-            return FALSE;
-        if (name[1] == '.' && name[2] == '\0')
-            return FALSE;
-    }
-    n = name;
-    do {
-        if (*n == '/')
-            return FALSE;
-    } while (*(++n) != '\0');
-
-    return TRUE;
-}
-
-const char *
-NM_ASSERT_VALID_PATH_COMPONENT(const char *name)
-{
-    if (G_LIKELY(nm_utils_is_valid_path_component(name)))
-        return name;
-
-    nm_log_err(LOGD_CORE,
-               "Failed asserting path component: %s%s%s",
-               NM_PRINT_FMT_QUOTED(name, "\"", name, "\"", "(null)"));
-    g_error("FATAL: Failed asserting path component: %s%s%s",
-            NM_PRINT_FMT_QUOTED(name, "\"", name, "\"", "(null)"));
-    g_assert_not_reached();
-}
-
 /*****************************************************************************/
 
 typedef struct {
@@ -2697,7 +2405,7 @@ _uuid_data_init(UuidData *uuid_data, gboolean packed, gboolean is_fake, const NM
         nm_utils_bin2hexstr_full(uuid, sizeof(*uuid), '\0', FALSE, uuid_data->str);
     } else {
         G_STATIC_ASSERT_EXPR(sizeof(uuid_data->str) >= 37);
-        _nm_utils_uuid_unparse(uuid, uuid_data->str);
+        nm_uuid_unparse(uuid, uuid_data->str);
     }
     return uuid_data;
 }
@@ -2750,7 +2458,7 @@ again:
                                          (guint8 *) &uuid,
                                          sizeof(uuid),
                                          NULL)) {
-                if (!nm_utils_uuid_is_null(&uuid)) {
+                if (!nm_uuid_is_null(&uuid)) {
                     /* an all-zero machine-id is not valid. */
                     is_fake = FALSE;
                 }
@@ -2795,11 +2503,11 @@ again:
 
             /* the fake machine-id is based on secret-key/boot-id, but we hash it
              * again, so that they are not literally the same. */
-            nm_utils_uuid_generate_from_string_bin(&uuid,
-                                                   (const char *) seed_bin,
-                                                   seed_len,
-                                                   NM_UTILS_UUID_TYPE_VERSION5,
-                                                   (gpointer) hash_seed);
+            nm_uuid_generate_from_string(&uuid,
+                                         (const char *) seed_bin,
+                                         seed_len,
+                                         NM_UUID_TYPE_VERSION5,
+                                         (gpointer) hash_seed);
         }
 
         if (!g_once_init_enter(&lock))
@@ -2884,7 +2592,7 @@ _host_id_read_timestamp(gboolean      use_secret_key_file,
     now = time(NULL);
     *out_timestamp_ns =
         NM_MAX((gint64) 1,
-               (now * NM_UTILS_NSEC_PER_SEC) - ((gint64)(v % ((guint64)(EPOCH_TWO_YEARS)))));
+               (now * NM_UTILS_NSEC_PER_SEC) - ((gint64) (v % ((guint64) (EPOCH_TWO_YEARS)))));
     return FALSE;
 }
 
@@ -3027,6 +2735,7 @@ _host_id_read(guint8 **out_host_id, gsize *out_host_id_len)
                                                len,
                                                0600,
                                                NULL,
+                                               NULL,
                                                &error)) {
             nm_log_warn(
                 LOGD_CORE,
@@ -3148,10 +2857,10 @@ again:
                                    NULL,
                                    NULL,
                                    NULL);
-        if (!contents || !_nm_utils_uuid_parse(nm_strstrip(contents), &uuid)) {
+        if (!contents || !nm_uuid_parse(nm_strstrip(contents), &uuid)) {
             /* generate a random UUID instead. */
             is_fake = TRUE;
-            _nm_utils_uuid_generate_random(&uuid);
+            nm_uuid_generate_random(&uuid);
         }
 
         if (!g_once_init_enter(&lock))
@@ -3407,89 +3116,6 @@ nm_utils_get_ipv6_interface_identifier(NMLinkType          link_type,
 
 /*****************************************************************************/
 
-/**
- * nm_utils_ipv6_addr_set_interface_identifier:
- * @addr: output token encoded as %in6_addr
- * @iid: %NMUtilsIPv6IfaceId interface identifier
- *
- * Converts the %NMUtilsIPv6IfaceId to an %in6_addr (suitable for use
- * with Linux platform). This only copies the lower 8 bytes, ignoring
- * the /64 network prefix which is expected to be all-zero for a valid
- * token.
- */
-void
-nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr *addr, const NMUtilsIPv6IfaceId iid)
-{
-    memcpy(addr->s6_addr + 8, &iid.id_u8, 8);
-}
-
-/**
- * nm_utils_ipv6_interface_identifier_get_from_addr:
- * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token
- * @addr: token encoded as %in6_addr
- *
- * Converts the %in6_addr encoded token (as used by Linux platform) to
- * the interface identifier.
- */
-void
-nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId *   iid,
-                                                 const struct in6_addr *addr)
-{
-    memcpy(iid, addr->s6_addr + 8, 8);
-}
-
-/**
- * nm_utils_ipv6_interface_identifier_get_from_token:
- * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token
- * @token: token encoded as string
- *
- * Converts the %in6_addr encoded token (as used in ip6 settings) to
- * the interface identifier.
- *
- * Returns: %TRUE if the @token is a valid token, %FALSE otherwise
- */
-gboolean
-nm_utils_ipv6_interface_identifier_get_from_token(NMUtilsIPv6IfaceId *iid, const char *token)
-{
-    struct in6_addr i6_token;
-
-    g_return_val_if_fail(token, FALSE);
-
-    if (!inet_pton(AF_INET6, token, &i6_token))
-        return FALSE;
-
-    if (!_nm_utils_inet6_is_token(&i6_token))
-        return FALSE;
-
-    nm_utils_ipv6_interface_identifier_get_from_addr(iid, &i6_token);
-    return TRUE;
-}
-
-/**
- * nm_utils_inet6_interface_identifier_to_token:
- * @iid: %NMUtilsIPv6IfaceId interface identifier
- * @buf: the destination buffer of at least %NM_UTILS_INET_ADDRSTRLEN
- *   bytes.
- *
- * Converts the interface identifier to a string token.
- *
- * Returns: the input buffer filled with the id as string.
- */
-const char *
-nm_utils_inet6_interface_identifier_to_token(NMUtilsIPv6IfaceId iid,
-                                             char               buf[static INET6_ADDRSTRLEN])
-{
-    struct in6_addr i6_token = {.s6_addr = {
-                                    0,
-                                }};
-
-    nm_assert(buf);
-    nm_utils_ipv6_addr_set_interface_identifier(&i6_token, iid);
-    return _nm_utils_inet6_ntop(&i6_token, buf);
-}
-
-/*****************************************************************************/
-
 char *
 nm_utils_stable_id_random(void)
 {
@@ -4269,89 +3895,6 @@ nm_utils_g_value_set_strv(GValue *value, GPtrArray *strings)
 
 /*****************************************************************************/
 
-/**
- * Takes a pair @timestamp and @duration, and returns the remaining duration based
- * on the new timestamp @now.
- */
-guint32
-nm_utils_lifetime_rebase_relative_time_on_now(guint32 timestamp, guint32 duration, gint32 now)
-{
-    gint64 t;
-
-    nm_assert(now >= 0);
-
-    if (duration == NM_PLATFORM_LIFETIME_PERMANENT)
-        return NM_PLATFORM_LIFETIME_PERMANENT;
-
-    if (timestamp == 0) {
-        /* if the @timestamp is zero, assume it was just left unset and that the relative
-         * @duration starts counting from @now. This is convenient to construct an address
-         * and print it in nm_platform_ip4_address_to_string().
-         *
-         * In general it does not make sense to set the @duration without anchoring at
-         * @timestamp because you don't know the absolute expiration time when looking
-         * at the address at a later moment. */
-        timestamp = now;
-    }
-
-    /* For timestamp > now, just accept it and calculate the expected(?) result. */
-    t = (gint64) timestamp + (gint64) duration - (gint64) now;
-
-    if (t <= 0)
-        return 0;
-    if (t >= NM_PLATFORM_LIFETIME_PERMANENT)
-        return NM_PLATFORM_LIFETIME_PERMANENT - 1;
-    return t;
-}
-
-guint32
-nm_utils_lifetime_get(guint32  timestamp,
-                      guint32  lifetime,
-                      guint32  preferred,
-                      gint32   now,
-                      guint32 *out_preferred)
-{
-    guint32 t_lifetime, t_preferred;
-
-    nm_assert(now >= 0);
-
-    if (timestamp == 0 && lifetime == 0) {
-        /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy
-         * creation of such addresses (without requiring to set the lifetime fields to
-         * NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us
-         * to drop an address will have timestamp set.
-         */
-        NM_SET_OUT(out_preferred, NM_PLATFORM_LIFETIME_PERMANENT);
-        g_return_val_if_fail(preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT);
-        return NM_PLATFORM_LIFETIME_PERMANENT;
-    }
-
-    if (now <= 0)
-        now = nm_utils_get_monotonic_timestamp_sec();
-
-    t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now);
-    if (!t_lifetime) {
-        NM_SET_OUT(out_preferred, 0);
-        return 0;
-    }
-
-    t_preferred = nm_utils_lifetime_rebase_relative_time_on_now(timestamp, preferred, now);
-
-    NM_SET_OUT(out_preferred, MIN(t_preferred, t_lifetime));
-
-    /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
-     * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
-     * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
-     */
-    g_return_val_if_fail(timestamp != 0
-                             || (lifetime == NM_PLATFORM_LIFETIME_PERMANENT
-                                 && preferred == NM_PLATFORM_LIFETIME_PERMANENT),
-                         t_lifetime);
-    g_return_val_if_fail(t_preferred <= t_lifetime, t_lifetime);
-
-    return t_lifetime;
-}
-
 const char *
 nm_utils_dnsmasq_status_to_string(int status, char *dest, gsize size)
 {
@@ -5065,9 +4608,12 @@ nm_wifi_utils_parse_ies(const guint8 *bytes,
         case WLAN_EID_VENDOR_SPECIFIC:
             if (len == 8 && bytes[0] == 0x00 /* OUI: Microsoft */
                 && bytes[1] == 0x50 && bytes[2] == 0xf2
-                && bytes[3] == 0x11)                     /* OUI type: Network cost */
-                NM_SET_OUT(out_metered, (bytes[7] > 1)); /* Cost level > 1 */
-            if (elem_len >= 10 && bytes[0] == 0x50       /* OUI: WiFi Alliance */
+                && bytes[3] == 0x11) /* OUI type: Network cost */
+            {
+                /* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nct/ */
+                NM_SET_OUT(out_metered, (bytes[4] > 1)); /* Cost level > 1 */
+            }
+            if (elem_len >= 10 && bytes[0] == 0x50 /* OUI: WiFi Alliance */
                 && bytes[1] == 0x6f && bytes[2] == 0x9a
                 && bytes[3] == 0x1c) /* OUI type: OWE Transition Mode */
                 NM_SET_OUT(out_owe_transition_mode, TRUE);
@@ -5102,16 +4648,329 @@ nm_wifi_utils_level_to_quality(int val)
 
 /*****************************************************************************/
 
-NM_UTILS_ENUM2STR_DEFINE(nm_icmpv6_router_pref_to_string,
-                         NMIcmpv6RouterPref,
-                         NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_LOW, "low"),
-                         NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"),
-                         NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_HIGH, "high"),
-                         NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), );
-
 NM_UTILS_LOOKUP_STR_DEFINE(nm_activation_type_to_string,
                            NMActivationType,
                            NM_UTILS_LOOKUP_DEFAULT_WARN("(unknown)"),
                            NM_UTILS_LOOKUP_STR_ITEM(NM_ACTIVATION_TYPE_MANAGED, "managed"),
                            NM_UTILS_LOOKUP_STR_ITEM(NM_ACTIVATION_TYPE_ASSUME, "assume"),
                            NM_UTILS_LOOKUP_STR_ITEM(NM_ACTIVATION_TYPE_EXTERNAL, "external"), );
+
+/*****************************************************************************/
+
+typedef struct {
+    GPid     pid;
+    GTask *  task;
+    gulong   cancellable_id;
+    GSource *child_watch_source;
+    GSource *timeout_source;
+
+    int      child_stdin;
+    int      child_stdout;
+    GSource *input_source;
+    GSource *output_source;
+
+    NMStrBuf in_buffer;
+    NMStrBuf out_buffer;
+    gsize    out_buffer_offset;
+} HelperInfo;
+
+#define _NMLOG_PREFIX_NAME "helper"
+#define _NMLOG_DOMAIN      LOGD_CORE
+#define _NMLOG2(level, info, ...)                                                   \
+    G_STMT_START                                                                    \
+    {                                                                               \
+        if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) {                         \
+            HelperInfo *_info = (info);                                             \
+                                                                                    \
+            _nm_log((level),                                                        \
+                    (_NMLOG_DOMAIN),                                                \
+                    0,                                                              \
+                    NULL,                                                           \
+                    NULL,                                                           \
+                    _NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT                \
+                                       ",%d]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                    NM_HASH_OBFUSCATE_PTR(_info),                                   \
+                    _info->pid _NM_UTILS_MACRO_REST(__VA_ARGS__));                  \
+        }                                                                           \
+    }                                                                               \
+    G_STMT_END
+
+static void
+helper_info_free(gpointer data)
+{
+    HelperInfo *info = data;
+
+    nm_clear_g_source_inst(&info->child_watch_source);
+    nm_clear_g_source_inst(&info->timeout_source);
+    g_object_unref(info->task);
+
+    nm_str_buf_destroy(&info->in_buffer);
+    nm_str_buf_destroy(&info->out_buffer);
+    nm_clear_g_source_inst(&info->input_source);
+    nm_clear_g_source_inst(&info->output_source);
+
+    if (info->child_stdout != -1)
+        nm_close(info->child_stdout);
+    if (info->child_stdin != -1)
+        nm_close(info->child_stdin);
+
+    if (info->pid != -1) {
+        nm_assert(info->pid > 1);
+        nm_utils_kill_child_async(info->pid, SIGKILL, LOGD_CORE, _NMLOG_PREFIX_NAME, 0, NULL, NULL);
+    }
+
+    g_free(info);
+}
+
+static void
+helper_complete(HelperInfo *info, GError *error)
+{
+    if (error) {
+        nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task),
+                                          &info->cancellable_id);
+        g_task_return_error(info->task, error);
+        helper_info_free(info);
+        return;
+    }
+
+    if (info->input_source || info->output_source || info->pid != -1) {
+        /* Wait that pipes are closed and process has terminated */
+        return;
+    }
+
+    nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
+    g_task_return_pointer(info->task, nm_str_buf_finalize(&info->in_buffer, NULL), g_free);
+    helper_info_free(info);
+}
+
+static gboolean
+helper_can_write(int fd, GIOCondition condition, gpointer user_data)
+{
+    HelperInfo *info = user_data;
+    gssize      n_written;
+    int         errsv;
+
+    if (NM_FLAGS_HAS(condition, G_IO_ERR)) {
+        errsv = EIO;
+        goto out_error;
+    } else if (NM_FLAGS_HAS(condition, G_IO_HUP)) {
+        errsv = EPIPE;
+        goto out_error;
+    }
+
+    n_written = write(info->child_stdin,
+                      &((nm_str_buf_get_str_unsafe(&info->out_buffer))[info->out_buffer_offset]),
+                      info->out_buffer.len - info->out_buffer_offset);
+    errsv     = errno;
+
+    if (n_written < 0 && errsv != EAGAIN)
+        goto out_error;
+
+    if (n_written > 0) {
+        if ((gsize) n_written >= (info->out_buffer.len - info->out_buffer_offset)) {
+            nm_assert((gsize) n_written == (info->out_buffer.len - info->out_buffer_offset));
+            nm_clear_g_source_inst(&info->output_source);
+            nm_close(info->child_stdin);
+            info->child_stdin = -1;
+            return G_SOURCE_CONTINUE;
+        }
+        info->out_buffer_offset += (gsize) n_written;
+    }
+
+    return G_SOURCE_CONTINUE;
+
+out_error:
+    nm_clear_g_source_inst(&info->output_source);
+    helper_complete(info,
+                    g_error_new(NM_UTILS_ERROR,
+                                NM_UTILS_ERROR_UNKNOWN,
+                                "error writing to helper: %d (%s)",
+                                errsv,
+                                nm_strerror_native(errsv)));
+    return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+helper_have_data(int fd, GIOCondition condition, gpointer user_data)
+{
+    HelperInfo *info = user_data;
+    gssize      n_read;
+    GError *    error = NULL;
+
+    n_read = nm_utils_fd_read(fd, &info->in_buffer);
+    _LOG2T(info, "read returns %ld", (long) n_read);
+
+    if (n_read > 0)
+        return G_SOURCE_CONTINUE;
+
+    nm_clear_g_source_inst(&info->input_source);
+    nm_close(info->child_stdout);
+    info->child_stdout = -1;
+
+    _LOG2T(info, "stdout closed");
+
+    if (n_read < 0) {
+        error = g_error_new(NM_UTILS_ERROR,
+                            NM_UTILS_ERROR_UNKNOWN,
+                            "read from process returned %d (%s)",
+                            (int) -n_read,
+                            nm_strerror_native((int) -n_read));
+    }
+
+    helper_complete(info, error);
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+helper_child_terminated(GPid pid, int status, gpointer user_data)
+{
+    HelperInfo *  info        = user_data;
+    GError *      error       = NULL;
+    gs_free char *status_desc = NULL;
+
+    _LOG2D(info, "process %s", (status_desc = nm_utils_get_process_exit_status_desc(status)));
+
+    info->pid = -1;
+    nm_clear_g_source_inst(&info->child_watch_source);
+
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        if (!status_desc)
+            status_desc = nm_utils_get_process_exit_status_desc(status);
+        error =
+            g_error_new(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "helper process %s", status_desc);
+    }
+
+    helper_complete(info, error);
+}
+
+static gboolean
+helper_timeout(gpointer user_data)
+{
+    HelperInfo *info = user_data;
+
+    nm_clear_g_source_inst(&info->timeout_source);
+    helper_complete(info, g_error_new_literal(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "timed out"));
+
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+helper_cancelled(GObject *object, gpointer user_data)
+{
+    HelperInfo *info  = user_data;
+    GError *    error = NULL;
+
+    nm_clear_g_signal_handler(g_task_get_cancellable(info->task), &info->cancellable_id);
+    nm_utils_error_set_cancelled(&error, FALSE, NULL);
+    helper_complete(info, error);
+}
+
+void
+nm_utils_spawn_helper(const char *const * args,
+                      GCancellable *      cancellable,
+                      GAsyncReadyCallback callback,
+                      gpointer            cb_data)
+{
+    gs_free_error GError *error    = NULL;
+    gs_free char *        commands = NULL;
+    HelperInfo *          info;
+    int                   fd_flags;
+    const char *const *   arg;
+
+    nm_assert(args && args[0]);
+
+    info  = g_new(HelperInfo, 1);
+    *info = (HelperInfo){
+        .task         = nm_g_task_new(NULL, cancellable, nm_utils_spawn_helper, callback, cb_data),
+        .child_stdin  = -1,
+        .child_stdout = -1,
+        .pid          = -1,
+    };
+
+    if (!g_spawn_async_with_pipes("/",
+                                  (char **) NM_MAKE_STRV(LIBEXECDIR "/nm-daemon-helper"),
+                                  (char **) NM_MAKE_STRV(),
+                                  G_SPAWN_DO_NOT_REAP_CHILD,
+                                  NULL,
+                                  NULL,
+                                  &info->pid,
+                                  &info->child_stdin,
+                                  &info->child_stdout,
+                                  NULL,
+                                  &error)) {
+        info->child_stdin  = -1;
+        info->child_stdout = -1;
+        info->pid          = -1;
+        g_task_return_error(info->task,
+                            g_error_new(NM_UTILS_ERROR,
+                                        NM_UTILS_ERROR_UNKNOWN,
+                                        "error spawning nm-helper: %s",
+                                        error->message));
+        helper_info_free(info);
+        return;
+    }
+
+    _LOG2D(info, "spawned process with args: %s", (commands = g_strjoinv(" ", (char **) args)));
+
+    info->child_watch_source = g_child_watch_source_new(info->pid);
+    g_source_set_callback(info->child_watch_source,
+                          G_SOURCE_FUNC(helper_child_terminated),
+                          info,
+                          NULL);
+    g_source_attach(info->child_watch_source, g_main_context_get_thread_default());
+
+    info->timeout_source =
+        nm_g_timeout_source_new_seconds(20, G_PRIORITY_DEFAULT, helper_timeout, info, NULL);
+    g_source_attach(info->timeout_source, g_main_context_get_thread_default());
+
+    /* Set file descriptors as non-blocking */
+    fd_flags = fcntl(info->child_stdin, F_GETFD, 0);
+    fcntl(info->child_stdin, F_SETFL, fd_flags | O_NONBLOCK);
+    fd_flags = fcntl(info->child_stdout, F_GETFD, 0);
+    fcntl(info->child_stdout, F_SETFL, fd_flags | O_NONBLOCK);
+
+    /* Watch process stdin */
+    nm_str_buf_init(&info->out_buffer, 32, TRUE);
+    for (arg = args; *arg; arg++) {
+        nm_str_buf_append(&info->out_buffer, *arg);
+        nm_str_buf_append_c(&info->out_buffer, '\0');
+    }
+    info->output_source = nm_g_unix_fd_source_new(info->child_stdin,
+                                                  G_IO_OUT | G_IO_ERR | G_IO_HUP,
+                                                  G_PRIORITY_DEFAULT,
+                                                  helper_can_write,
+                                                  info,
+                                                  NULL);
+    g_source_attach(info->output_source, g_main_context_get_thread_default());
+
+    /* Watch process stdout */
+    nm_str_buf_init(&info->in_buffer, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
+    info->input_source = nm_g_unix_fd_source_new(info->child_stdout,
+                                                 G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                                 G_PRIORITY_DEFAULT,
+                                                 helper_have_data,
+                                                 info,
+                                                 NULL);
+    g_source_attach(info->input_source, g_main_context_get_thread_default());
+
+    if (cancellable) {
+        gulong signal_id;
+
+        signal_id = g_cancellable_connect(cancellable, G_CALLBACK(helper_cancelled), info, NULL);
+        if (signal_id == 0) {
+            /* the request is already cancelled. Return. */
+            return;
+        }
+        info->cancellable_id = signal_id;
+    }
+}
+
+char *
+nm_utils_spawn_helper_finish(GAsyncResult *result, GError **error)
+{
+    GTask *task = G_TASK(result);
+
+    nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_spawn_helper));
+
+    return g_task_propagate_pointer(task, error);
+}
diff --git a/src/core/nm-core-utils.h b/src/core/nm-core-utils.h
index 5dc4d432..bcb182b8 100644
--- a/src/core/nm-core-utils.h
+++ b/src/core/nm-core-utils.h
@@ -12,12 +12,10 @@
 
 #include "nm-connection.h"
 
-#include "nm-glib-aux/nm-time-utils.h"
+#include "libnm-glib-aux/nm-time-utils.h"
 
 /*****************************************************************************/
 
-#define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32
-
 #define NM_DEFINE_SINGLETON_INSTANCE(TYPE) static TYPE *singleton_instance
 
 #define NM_DEFINE_SINGLETON_REGISTER(TYPE)                                                      \
@@ -98,73 +96,6 @@ void _nm_singleton_instance_register_destruction(GObject *instance);
 gboolean nm_ether_addr_is_valid(const NMEtherAddr *addr);
 gboolean nm_ether_addr_is_valid_str(const char *str);
 
-gconstpointer
-nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen);
-in_addr_t              nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen);
-const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr *      dst,
-                                                               const struct in6_addr *src,
-                                                               guint8                 plen);
-
-static inline int
-nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint8 plen)
-{
-    NM_CMP_DIRECT(htonl(nm_utils_ip4_address_clear_host_address(addr_a, plen)),
-                  htonl(nm_utils_ip4_address_clear_host_address(addr_b, plen)));
-    return 0;
-}
-
-int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
-                                         const struct in6_addr *addr_b,
-                                         guint8                 plen);
-
-static inline int
-nm_utils_ip_address_same_prefix_cmp(int           addr_family,
-                                    gconstpointer addr_a,
-                                    gconstpointer addr_b,
-                                    guint8        plen)
-{
-    nm_assert_addr_family(addr_family);
-
-    NM_CMP_SELF(addr_a, addr_b);
-
-    if (NM_IS_IPv4(addr_family)) {
-        return nm_utils_ip4_address_same_prefix_cmp(*((const in_addr_t *) addr_a),
-                                                    *((const in_addr_t *) addr_b),
-                                                    plen);
-    }
-
-    return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen);
-}
-
-static inline gboolean
-nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint8 plen)
-{
-    return nm_utils_ip4_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
-}
-
-static inline gboolean
-nm_utils_ip6_address_same_prefix(const struct in6_addr *addr_a,
-                                 const struct in6_addr *addr_b,
-                                 guint8                 plen)
-{
-    return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
-}
-
-static inline gboolean
-nm_utils_ip_address_same_prefix(int           addr_family,
-                                gconstpointer addr_a,
-                                gconstpointer addr_b,
-                                guint8        plen)
-{
-    return nm_utils_ip_address_same_prefix_cmp(addr_family, addr_a, addr_b, plen) == 0;
-}
-
-#define NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a, b, plen) \
-    NM_CMP_RETURN(nm_utils_ip4_address_same_prefix_cmp((a), (b), (plen)))
-
-#define NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(a, b, plen) \
-    NM_CMP_RETURN(nm_utils_ip6_address_same_prefix_cmp((a), (b), (plen)))
-
 static inline void
 nm_hash_update_in6addr(NMHashState *h, const struct in6_addr *addr)
 {
@@ -185,8 +116,6 @@ nm_hash_update_in6addr_prefix(NMHashState *h, const struct in6_addr *addr, guint
     nm_hash_update_in6addr(h, &a);
 }
 
-double nm_utils_exp10(gint16 e);
-
 /**
  * nm_utils_ip6_route_metric_normalize:
  * @metric: the route metric
@@ -221,9 +150,6 @@ nm_utils_ip_route_metric_penalize(guint32 metric, guint32 penalty)
     return G_MAXUINT32;
 }
 
-int nm_utils_modprobe(GError **error, gboolean suppress_error_loggin, const char *arg1, ...)
-    G_GNUC_NULL_TERMINATED;
-
 void nm_utils_kill_process_sync(pid_t       pid,
                                 guint64     start_time,
                                 int         sig,
@@ -291,9 +217,6 @@ gboolean nm_utils_connection_has_default_route(NMConnection *connection,
                                                int           addr_family,
                                                gboolean *    out_is_never_default);
 
-char *      nm_utils_new_vlan_name(const char *parent_iface, guint32 vlan_id);
-const char *nm_utils_new_infiniband_name(char *name, const char *parent_name, int p_key);
-
 int nm_utils_cmp_connection_by_autoconnect_priority(NMConnection *a, NMConnection *b);
 
 void nm_utils_log_connection_diff(NMConnection *connection,
@@ -304,19 +227,6 @@ void nm_utils_log_connection_diff(NMConnection *connection,
                                   const char *  prefix,
                                   const char *  dbus_path);
 
-gboolean    nm_utils_is_valid_path_component(const char *name);
-const char *NM_ASSERT_VALID_PATH_COMPONENT(const char *name);
-
-#define NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE 100
-
-const char *
-nm_utils_sysctl_ip_conf_path(int addr_family, char *buf, const char *ifname, const char *property);
-
-gboolean nm_utils_sysctl_ip_conf_is_path(int         addr_family,
-                                         const char *path,
-                                         const char *ifname,
-                                         const char *property);
-
 gboolean nm_utils_is_specific_hostname(const char *name);
 
 struct _NMUuid;
@@ -344,49 +254,6 @@ nm_utils_arp_type_get_hwaddr_relevant_part(int arp_type, const guint8 **hwaddr,
 
 /*****************************************************************************/
 
-/* IPv6 Interface Identifier helpers */
-
-/**
- * NMUtilsIPv6IfaceId:
- * @id: convenience member for validity checking; never use directly
- * @id_u8: the 64-bit Interface Identifier
- *
- * Holds a 64-bit IPv6 Interface Identifier.  The IID is a sequence of bytes
- * and should not normally be treated as a %guint64, but this is done for
- * convenience of validity checking and initialization.
- */
-struct _NMUtilsIPv6IfaceId {
-    union {
-        guint64 id;
-        guint8  id_u8[8];
-    };
-};
-
-#define NM_UTILS_IPV6_IFACE_ID_INIT \
-    {                               \
-        {                           \
-            .id = 0                 \
-        }                           \
-    }
-
-void nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr *        addr,
-                                                 const NMUtilsIPv6IfaceId iid);
-
-void nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId *   iid,
-                                                      const struct in6_addr *addr);
-
-gboolean nm_utils_ipv6_interface_identifier_get_from_token(NMUtilsIPv6IfaceId *iid,
-                                                           const char *        token);
-
-const char *nm_utils_inet6_interface_identifier_to_token(NMUtilsIPv6IfaceId iid,
-                                                         char buf[static INET6_ADDRSTRLEN]);
-
-gboolean nm_utils_get_ipv6_interface_identifier(NMLinkType          link_type,
-                                                const guint8 *      hwaddr,
-                                                guint               len,
-                                                guint               dev_id,
-                                                NMUtilsIPv6IfaceId *out_iid);
-
 typedef enum {
     /* The stable type. Note that this value is encoded in the
      * generated addresses, thus the numbers MUST not change.
@@ -501,33 +368,6 @@ void             _nm_utils_set_testing(NMUtilsTestFlags flags);
 
 void nm_utils_g_value_set_strv(GValue *value, GPtrArray *strings);
 
-guint32
-nm_utils_lifetime_rebase_relative_time_on_now(guint32 timestamp, guint32 duration, gint32 now);
-
-guint32 nm_utils_lifetime_get(guint32  timestamp,
-                              guint32  lifetime,
-                              guint32  preferred,
-                              gint32   now,
-                              guint32 *out_preferred);
-
-/*****************************************************************************/
-
-#define NM_IPV4LL_NETWORK ((in_addr_t)(htonl(0xA9FE0000lu)))
-#define NM_IPV4LL_NETMASK ((in_addr_t)(htonl(0xFFFF0000lu)))
-
-static inline gboolean
-nm_utils_ip4_address_is_link_local(in_addr_t addr)
-{
-    return (addr & NM_IPV4LL_NETMASK) == NM_IPV4LL_NETWORK;
-}
-
-static inline gboolean
-nm_utils_ip4_address_is_zeronet(in_addr_t network)
-{
-    /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */
-    return (network & htonl(0xFF000000u)) == htonl(0x00000000u);
-}
-
 /*****************************************************************************/
 
 const char *nm_utils_dnsmasq_status_to_string(int status, char *dest, gsize size);
@@ -556,20 +396,6 @@ char *   nm_utils_format_con_diff_for_audit(GHashTable *diff);
 
 /*****************************************************************************/
 
-/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from <linux/icmpv6.h>,
- * the values for netlink attribute RTA_PREF) and "enum ndp_route_preference"
- * from <ndp.h>. */
-typedef enum _nm_packed {
-    NM_ICMPV6_ROUTER_PREF_MEDIUM  = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */
-    NM_ICMPV6_ROUTER_PREF_LOW     = 0x3, /* ICMPV6_ROUTER_PREF_LOW */
-    NM_ICMPV6_ROUTER_PREF_HIGH    = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */
-    NM_ICMPV6_ROUTER_PREF_INVALID = 0x2, /* ICMPV6_ROUTER_PREF_INVALID */
-} NMIcmpv6RouterPref;
-
-const char *nm_icmpv6_router_pref_to_string(NMIcmpv6RouterPref pref, char *buf, gsize len);
-
-/*****************************************************************************/
-
 const char *nm_activation_type_to_string(NMActivationType activation_type);
 
 /*****************************************************************************/
@@ -594,4 +420,13 @@ guint8 nm_wifi_utils_level_to_quality(int val);
 #define NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN  "Unable to determine UID of the request"
 #define NM_UTILS_ERROR_MSG_INSUFF_PRIV     "Insufficient privileges"
 
+/*****************************************************************************/
+
+void nm_utils_spawn_helper(const char *const * args,
+                           GCancellable *      cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer            cb_data);
+
+char *nm_utils_spawn_helper_finish(GAsyncResult *result, GError **error);
+
 #endif /* __NM_CORE_UTILS_H__ */
diff --git a/src/core/nm-dbus-manager.c b/src/core/nm-dbus-manager.c
index 4f34ee52..c47d4dbf 100644
--- a/src/core/nm-dbus-manager.c
+++ b/src/core/nm-dbus-manager.c
@@ -13,13 +13,13 @@
 #include <sys/types.h>
 
 #include "c-list/src/c-list.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-dbus-interface.h"
-#include "nm-core-internal.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 #include "nm-dbus-object.h"
 #include "NetworkManagerUtils.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 
 /* The base path for our GDBusObjectManagerServers.  They do not contain
  * "NetworkManager" because GDBusObjectManagerServer requires that all
@@ -1139,10 +1139,6 @@ _nm_dbus_manager_obj_notify(NMDBusObject *obj, guint n_pspecs, const GParamSpec
     NMDBusManagerPrivate *priv;
     RegistrationData *    reg_data;
     guint                 i, p;
-    gboolean              any_legacy_signals    = FALSE;
-    gboolean              any_legacy_properties = FALSE;
-    GVariantBuilder       legacy_builder;
-    GVariant *            device_statistics_args = NULL;
 
     nm_assert(NM_IS_DBUS_OBJECT(obj));
     nm_assert(obj->internal.path);
@@ -1159,13 +1155,6 @@ _nm_dbus_manager_obj_notify(NMDBusObject *obj, guint n_pspecs, const GParamSpec
     if (G_UNLIKELY(!priv->started))
         return;
 
-    c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) {
-        if (_reg_data_get_interface_info(reg_data)->legacy_property_changed) {
-            any_legacy_signals = TRUE;
-            break;
-        }
-    }
-
     /* do a naive search for the matching NMDBusPropertyInfoExtended infos. Since the number of
      * (interfaces x properties) is static and possibly small, this naive search is effectively
      * O(1). We might wanna introduce some index to lookup the properties in question faster.
@@ -1196,18 +1185,6 @@ _nm_dbus_manager_obj_notify(NMDBusObject *obj, guint n_pspecs, const GParamSpec
 
                 value = _obj_get_property(reg_data, i, TRUE);
 
-                if (property_info->include_in_legacy_property_changed && any_legacy_signals) {
-                    /* also track the value in the legacy_builder to emit legacy signals below. */
-                    if (!any_legacy_properties) {
-                        any_legacy_properties = TRUE;
-                        g_variant_builder_init(&legacy_builder, G_VARIANT_TYPE("a{sv}"));
-                    }
-                    g_variant_builder_add(&legacy_builder,
-                                          "{sv}",
-                                          property_info->parent.name,
-                                          value);
-                }
-
                 if (!has_properties) {
                     has_properties = TRUE;
                     g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
@@ -1221,74 +1198,16 @@ _nm_dbus_manager_obj_notify(NMDBusObject *obj, guint n_pspecs, const GParamSpec
 
         args = g_variant_builder_end(&builder);
 
-        if (G_UNLIKELY(interface_info == &nm_interface_info_device_statistics)) {
-            /* we treat the Device.Statistics signal special, because we need to
-             * emit a signal also for it (below). */
-            nm_assert(!device_statistics_args);
-            device_statistics_args = g_variant_ref_sink(args);
-        }
-
         g_variant_builder_init(&invalidated_builder, G_VARIANT_TYPE("as"));
         g_dbus_connection_emit_signal(
             priv->main_dbus_connection,
             NULL,
             obj->internal.path,
-            "org.freedesktop.DBus.Properties",
+            DBUS_INTERFACE_PROPERTIES,
             "PropertiesChanged",
             g_variant_new("(s@a{sv}as)", interface_info->parent.name, args, &invalidated_builder),
             NULL);
     }
-
-    if (G_UNLIKELY(device_statistics_args)) {
-        /* this is a special interface: it has a legacy PropertiesChanged signal,
-         * however, contrary to other interfaces with ~regular~ legacy signals,
-         * we only notify about properties that actually belong to this interface. */
-        g_dbus_connection_emit_signal(priv->main_dbus_connection,
-                                      NULL,
-                                      obj->internal.path,
-                                      nm_interface_info_device_statistics.parent.name,
-                                      "PropertiesChanged",
-                                      g_variant_new("(@a{sv})", device_statistics_args),
-                                      NULL);
-        g_variant_unref(device_statistics_args);
-    }
-
-    if (any_legacy_properties) {
-        gs_unref_variant GVariant *args = NULL;
-
-        /* The legacy PropertyChanged signal on the NetworkManager D-Bus interface is
-         * deprecated for the standard signal on org.freedesktop.DBus.Properties. However,
-         * for backward compatibility, we still need to emit it.
-         *
-         * Due to a bug in dbus-glib in NetworkManager <= 1.0, the signal would
-         * not only notify about properties that were actually on the corresponding
-         * D-Bus interface. Instead, it would notify about all relevant properties
-         * on all interfaces that had such a signal.
-         *
-         * For example, "HwAddress" gets emitted both on "fdo.NM.Device.Ethernet"
-         * and "fdo.NM.Device.Veth" for veth interfaces, although only the former
-         * actually has such a property.
-         * Also note that "fdo.NM.Device" interface has no legacy signal. All notifications
-         * about its properties are instead emitted on the interfaces of the subtypes.
-         *
-         * See bgo#770629 and commit bef26a2e69f51259095fa080221db73de09fd38d.
-         */
-        args = g_variant_ref_sink(g_variant_new("(a{sv})", &legacy_builder));
-        c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) {
-            const NMDBusInterfaceInfoExtended *interface_info =
-                _reg_data_get_interface_info(reg_data);
-
-            if (interface_info->legacy_property_changed) {
-                g_dbus_connection_emit_signal(priv->main_dbus_connection,
-                                              NULL,
-                                              obj->internal.path,
-                                              interface_info->parent.name,
-                                              "PropertiesChanged",
-                                              args,
-                                              NULL);
-            }
-        }
-    }
 }
 
 void
diff --git a/src/core/nm-dbus-object.c b/src/core/nm-dbus-object.c
index 6f294fe2..04149739 100644
--- a/src/core/nm-dbus-object.c
+++ b/src/core/nm-dbus-object.c
@@ -183,23 +183,25 @@ nm_dbus_object_unexport_on_idle(gpointer /* (NMDBusObject *) */ self_take)
 
 /*****************************************************************************/
 
-void
+gboolean
 _nm_dbus_object_clear_and_unexport(NMDBusObject **location)
 {
     NMDBusObject *self;
 
-    g_return_if_fail(location);
+    g_return_val_if_fail(location, FALSE);
+
     if (!*location)
-        return;
+        return FALSE;
 
     self = g_steal_pointer(location);
 
-    g_return_if_fail(NM_IS_DBUS_OBJECT(self));
+    g_return_val_if_fail(NM_IS_DBUS_OBJECT(self), FALSE);
 
     if (self->internal.path)
         nm_dbus_object_unexport(self);
 
     g_object_unref(self);
+    return TRUE;
 }
 
 /*****************************************************************************/
diff --git a/src/core/nm-dbus-object.h b/src/core/nm-dbus-object.h
index d94bd5df..ae089dd3 100644
--- a/src/core/nm-dbus-object.h
+++ b/src/core/nm-dbus-object.h
@@ -45,17 +45,6 @@ typedef struct {
 
 /*****************************************************************************/
 
-/* "org.freedesktop.NetworkManager.Device.Statistics" is a special interface,
- * because although it has a legacy PropertiesChanged signal, it only notifies
- * about properties that actually exist on that interface. That is, because it
- * was added with 1.4.0 release, and thus didn't have the broken behavior like
- * other legacy interfaces. Those notify about *all* properties, even if they
- * are not part of that D-Bus interface. See also "include_in_legacy_property_changed"
- * and "legacy_property_changed". */
-extern const NMDBusInterfaceInfoExtended nm_interface_info_device_statistics;
-
-/*****************************************************************************/
-
 #define NM_TYPE_DBUS_OBJECT (nm_dbus_object_get_type())
 #define NM_DBUS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DBUS_OBJECT, NMDBusObject))
 #define NM_DBUS_OBJECT_CLASS(klass) \
@@ -166,9 +155,9 @@ void        nm_dbus_object_unexport(gpointer /* (NMDBusObject *) */ self);
 
 void nm_dbus_object_unexport_on_idle(gpointer /* (NMDBusObject *) */ self_take);
 
-void _nm_dbus_object_clear_and_unexport(NMDBusObject **location);
+gboolean _nm_dbus_object_clear_and_unexport(NMDBusObject **location);
 #define nm_dbus_object_clear_and_unexport(location) \
-    _nm_dbus_object_clear_and_unexport((NMDBusObject **) (location))
+    _nm_dbus_object_clear_and_unexport(NM_CAST_PPTR(NMDBusObject, (location)))
 
 void nm_dbus_object_emit_signal_variant(NMDBusObject *                     self,
                                         const NMDBusInterfaceInfoExtended *interface_info,
diff --git a/src/core/nm-dbus-utils.c b/src/core/nm-dbus-utils.c
index 5890bdf2..d3e5612d 100644
--- a/src/core/nm-dbus-utils.c
+++ b/src/core/nm-dbus-utils.c
@@ -11,10 +11,6 @@
 
 /*****************************************************************************/
 
-const GDBusSignalInfo nm_signal_info_property_changed_legacy = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT(
-    "PropertiesChanged",
-    .args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("properties", "a{sv}"), ), );
-
 GDBusPropertyInfo *
 nm_dbus_utils_interface_info_lookup_property(const GDBusInterfaceInfo *interface_info,
                                              const char *              property_name,
diff --git a/src/core/nm-dbus-utils.h b/src/core/nm-dbus-utils.h
index 501e1996..6585d43d 100644
--- a/src/core/nm-dbus-utils.h
+++ b/src/core/nm-dbus-utils.h
@@ -14,11 +14,6 @@ struct _NMDBusMethodInfoExtended;
 struct _NMDBusPropertyInfoExtendedBase {
     GDBusPropertyInfo _parent;
     const char *      property_name;
-
-    /* Whether the properties needs to be notified on the legacy
-     * PropertyChanged signal. This is only to preserve API, new
-     * properties should not use this. */
-    bool include_in_legacy_property_changed;
 };
 
 struct _NMDBusPropertyInfoExtendedReadWritable {
@@ -43,93 +38,46 @@ typedef struct {
         struct {
             GDBusPropertyInfo parent;
             const char *      property_name;
-
-            /* Whether the properties needs to be notified on the legacy
-             * PropertyChanged signal. This is only to preserve API, new
-             * properties should not use this. */
-            bool include_in_legacy_property_changed;
         };
     };
 } NMDBusPropertyInfoExtended;
 
 G_STATIC_ASSERT(G_STRUCT_OFFSET(NMDBusPropertyInfoExtended, property_name)
                 == G_STRUCT_OFFSET(struct _NMDBusPropertyInfoExtendedBase, property_name));
-G_STATIC_ASSERT(G_STRUCT_OFFSET(NMDBusPropertyInfoExtended, include_in_legacy_property_changed)
-                == G_STRUCT_OFFSET(struct _NMDBusPropertyInfoExtendedBase,
-                                   include_in_legacy_property_changed));
-
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL(m_name,                               \
-                                                            m_signature,                          \
-                                                            m_property_name,                      \
-                                                            m_include_in_legacy_property_changed) \
-    ((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedBase){                      \
-        ._parent =                                                                                \
-            {                                                                                     \
-                .ref_count = -1,                                                                  \
-                .name      = m_name,                                                              \
-                .signature = m_signature,                                                         \
-                .flags     = G_DBUS_PROPERTY_INFO_FLAGS_READABLE,                                 \
-            },                                                                                    \
-        .property_name                      = m_property_name,                                    \
-        .include_in_legacy_property_changed = m_include_in_legacy_property_changed,               \
-    }))
 
 #define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(m_name, m_signature, m_property_name) \
-    NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL(m_name, m_signature, m_property_name, FALSE)
-
-/* define a legacy property. Do not use for new code. */
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(m_name, m_signature, m_property_name) \
-    NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL(m_name, m_signature, m_property_name, TRUE)
-
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL(                            \
-    m_name,                                                                                 \
-    m_signature,                                                                            \
-    m_property_name,                                                                        \
-    m_permission,                                                                           \
-    m_audit_op,                                                                             \
-    m_include_in_legacy_property_changed)                                                   \
-    ((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedReadWritable){        \
-        ._base =                                                                            \
-            {                                                                               \
-                ._parent =                                                                  \
-                    {                                                                       \
-                        .ref_count = -1,                                                    \
-                        .name      = m_name,                                                \
-                        .signature = m_signature,                                           \
-                        .flags     = G_DBUS_PROPERTY_INFO_FLAGS_READABLE                    \
-                                 | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,                     \
-                    },                                                                      \
-                .property_name                      = m_property_name,                      \
-                .include_in_legacy_property_changed = m_include_in_legacy_property_changed, \
-            },                                                                              \
-        .permission = m_permission,                                                         \
-        .audit_op   = m_audit_op,                                                           \
+    ((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedBase){                 \
+        ._parent =                                                                           \
+            {                                                                                \
+                .ref_count = -1,                                                             \
+                .name      = m_name,                                                         \
+                .signature = m_signature,                                                    \
+                .flags     = G_DBUS_PROPERTY_INFO_FLAGS_READABLE,                            \
+            },                                                                               \
+        .property_name = m_property_name,                                                    \
     }))
 
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(m_name,           \
-                                                           m_signature,      \
-                                                           m_property_name,  \
-                                                           m_permission,     \
-                                                           m_audit_op)       \
-    NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL(m_name,          \
-                                                            m_signature,     \
-                                                            m_property_name, \
-                                                            m_permission,    \
-                                                            m_audit_op,      \
-                                                            FALSE)
-
-/* define a legacy property. Do not use for new code. */
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(m_name,          \
-                                                             m_signature,     \
-                                                             m_property_name, \
-                                                             m_permission,    \
-                                                             m_audit_op)      \
-    NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL(m_name,           \
-                                                            m_signature,      \
-                                                            m_property_name,  \
-                                                            m_permission,     \
-                                                            m_audit_op,       \
-                                                            TRUE)
+#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(m_name,                   \
+                                                           m_signature,              \
+                                                           m_property_name,          \
+                                                           m_permission,             \
+                                                           m_audit_op)               \
+    ((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedReadWritable){ \
+        ._base =                                                                     \
+            {                                                                        \
+                ._parent =                                                           \
+                    {                                                                \
+                        .ref_count = -1,                                             \
+                        .name      = m_name,                                         \
+                        .signature = m_signature,                                    \
+                        .flags     = G_DBUS_PROPERTY_INFO_FLAGS_READABLE             \
+                                 | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,              \
+                    },                                                               \
+                .property_name = m_property_name,                                    \
+            },                                                                       \
+        .permission = m_permission,                                                  \
+        .audit_op   = m_audit_op,                                                    \
+    }))
 
 typedef struct _NMDBusMethodInfoExtended {
     GDBusMethodInfo parent;
@@ -148,14 +96,8 @@ typedef struct _NMDBusMethodInfoExtended {
 
 typedef struct _NMDBusInterfaceInfoExtended {
     GDBusInterfaceInfo parent;
-
-    /* Whether the interface has a legacy property changed signal (@nm_signal_info_property_changed_legacy).
-     * New interfaces should not use this. */
-    bool legacy_property_changed : 1;
 } NMDBusInterfaceInfoExtended;
 
-extern const GDBusSignalInfo nm_signal_info_property_changed_legacy;
-
 #define NM_DBUS_INTERFACE_INFOS(...)                                           \
     ({                                                                         \
         static const NMDBusInterfaceInfoExtended *const _interface_infos[] = { \
diff --git a/src/core/nm-dcb.c b/src/core/nm-dcb.c
index b3958caa..930a1023 100644
--- a/src/core/nm-dcb.c
+++ b/src/core/nm-dcb.c
@@ -8,7 +8,7 @@
 #include <sys/wait.h>
 
 #include "nm-dcb.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "NetworkManagerUtils.h"
 
 static const char *helper_names[] = {"dcbtool", "fcoeadm"};
diff --git a/src/core/nm-default-daemon.h b/src/core/nm-default-daemon.h
index 65ca4b54..57d4c6af 100644
--- a/src/core/nm-default-daemon.h
+++ b/src/core/nm-default-daemon.h
@@ -8,7 +8,7 @@
 
 /*****************************************************************************/
 
-#include "nm-glib-aux/nm-default-glib-i18n-prog.h"
+#include "libnm-glib-aux/nm-default-glib-i18n-prog.h"
 
 #undef NETWORKMANAGER_COMPILATION
 #define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_DAEMON
@@ -19,7 +19,7 @@
 
 #include "nm-core-types.h"
 #include "nm-types.h"
-#include "nm-log-core/nm-logging.h"
+#include "libnm-log-core/nm-logging.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/nm-dhcp-config.c b/src/core/nm-dhcp-config.c
index 718d3c90..1cbad49e 100644
--- a/src/core/nm-dhcp-config.c
+++ b/src/core/nm-dhcp-config.c
@@ -59,6 +59,7 @@ struct _NMDhcpConfig {
 
 struct _NMDhcpConfigClass {
     NMDBusObjectClass parent;
+    int               addr_family;
 };
 
 G_DEFINE_ABSTRACT_TYPE(NMDhcpConfig, nm_dhcp_config, NM_TYPE_DBUS_OBJECT)
@@ -67,6 +68,14 @@ G_DEFINE_ABSTRACT_TYPE(NMDhcpConfig, nm_dhcp_config, NM_TYPE_DBUS_OBJECT)
 
 /*****************************************************************************/
 
+int
+nm_dhcp_config_get_addr_family(NMDhcpConfig *self)
+{
+    return NM_DHCP_CONFIG_GET_CLASS(self)->addr_family;
+}
+
+/*****************************************************************************/
+
 void
 nm_dhcp_config_set_options(NMDhcpConfig *self, GHashTable *options)
 {
@@ -116,8 +125,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 
     switch (prop_id) {
     case PROP_OPTIONS:
-        g_value_set_variant(value,
-                            priv->options ?: g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0));
+        g_value_set_variant(value, priv->options ?: nm_g_variant_singleton_aLsvI());
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -186,22 +194,23 @@ nm_dhcp4_config_init(NMDhcp4Config *self)
 static const NMDBusInterfaceInfoExtended interface_info_dhcp4_config = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DHCP4_CONFIG,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Options",
-                                                             "a{sv}",
-                                                             NM_DHCP_CONFIG_OPTIONS), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Options",
+                                                           "a{sv}",
+                                                           NM_DHCP_CONFIG_OPTIONS), ), ),
 };
 
 static void
 nm_dhcp4_config_class_init(NMDhcp4ConfigClass *klass)
 {
     NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
+    NMDhcpConfigClass *dhcp_config_class = NM_DHCP_CONFIG_CLASS(klass);
 
     dbus_object_class->export_path     = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/DHCP4Config");
     dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_dhcp4_config);
     dbus_object_class->export_on_construction = TRUE;
+
+    dhcp_config_class->addr_family = AF_INET;
 }
 
 /*****************************************************************************/
@@ -223,20 +232,21 @@ nm_dhcp6_config_init(NMDhcp6Config *self)
 static const NMDBusInterfaceInfoExtended interface_info_dhcp6_config = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DHCP6_CONFIG,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Options",
-                                                             "a{sv}",
-                                                             NM_DHCP_CONFIG_OPTIONS), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Options",
+                                                           "a{sv}",
+                                                           NM_DHCP_CONFIG_OPTIONS), ), ),
 };
 
 static void
 nm_dhcp6_config_class_init(NMDhcp6ConfigClass *klass)
 {
     NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
+    NMDhcpConfigClass *dhcp_config_class = NM_DHCP_CONFIG_CLASS(klass);
 
     dbus_object_class->export_path     = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/DHCP6Config");
     dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_dhcp6_config);
     dbus_object_class->export_on_construction = TRUE;
+
+    dhcp_config_class->addr_family = AF_INET6;
 }
diff --git a/src/core/nm-dispatcher.c b/src/core/nm-dispatcher.c
index 0ab409f5..32c9db62 100644
--- a/src/core/nm-dispatcher.c
+++ b/src/core/nm-dispatcher.c
@@ -8,7 +8,7 @@
 
 #include "nm-dispatcher.h"
 
-#include "nm-libnm-core-aux/nm-dispatcher-api.h"
+#include "libnm-core-aux-extern/nm-dispatcher-api.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-connectivity.h"
@@ -20,8 +20,8 @@
 #include "nm-ip6-config.h"
 #include "nm-manager.h"
 #include "settings/nm-settings-connection.h"
-#include "platform/nm-platform.h"
-#include "nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #define CALL_TIMEOUT (1000 * 60 * 10) /* 10 minutes for all scripts */
 
@@ -206,10 +206,7 @@ dump_ip_to_props(NMIPConfig *ip, GVariantBuilder *builder)
             array[0] = addr->a4.address;
             array[1] = addr->a4.plen;
             array[2] = gw;
-            g_variant_builder_add(
-                &int_builder,
-                "@au",
-                g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, array, 3, sizeof(guint32)));
+            g_variant_builder_add(&int_builder, "@au", nm_g_variant_new_au(array, 3));
         } else {
             const struct in6_addr *gw = &in6addr_any;
 
@@ -278,10 +275,7 @@ dump_ip_to_props(NMIPConfig *ip, GVariantBuilder *builder)
             array[1] = route->r4.plen;
             array[2] = route->r4.gateway;
             array[3] = route->r4.metric;
-            g_variant_builder_add(
-                &int_builder,
-                "@au",
-                g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, array, 4, sizeof(guint32)));
+            g_variant_builder_add(&int_builder, "@au", nm_g_variant_new_au(array, 4));
         } else {
             var1 = nm_g_variant_new_ay_in6addr(&route->r6.network);
             var2 = nm_g_variant_new_ay_in6addr(&route->r6.gateway);
@@ -550,9 +544,9 @@ _dispatcher_call(NMDispatcherAction    action,
 
     if (applied_connection)
         connection_dict =
-            nm_connection_to_dbus(applied_connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
+            nm_connection_to_dbus(applied_connection, NM_CONNECTION_SERIALIZE_WITH_NON_SECRET);
     else
-        connection_dict = g_variant_new_array(G_VARIANT_TYPE("{sa{sv}}"), NULL, 0);
+        connection_dict = nm_g_variant_singleton_aLsaLsvII();
 
     g_variant_builder_init(&connection_props, G_VARIANT_TYPE_VARDICT);
     if (settings_connection) {
@@ -620,8 +614,8 @@ _dispatcher_call(NMDispatcherAction    action,
                       &device_proxy_props,
                       &device_ip4_props,
                       &device_ip6_props,
-                      device_dhcp4_props ?: g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0),
-                      device_dhcp6_props ?: g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0),
+                      device_dhcp4_props ?: nm_g_variant_singleton_aLsvI(),
+                      device_dhcp6_props ?: nm_g_variant_singleton_aLsvI(),
                       connectivity_state_string,
                       vpn_iface ?: "",
                       &vpn_proxy_props,
diff --git a/src/core/nm-firewall-manager.h b/src/core/nm-firewall-manager.h
deleted file mode 100644
index 1ce802b2..00000000
--- a/src/core/nm-firewall-manager.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_FIREWALL_MANAGER_H__
-#define __NETWORKMANAGER_FIREWALL_MANAGER_H__
-
-#define NM_TYPE_FIREWALL_MANAGER (nm_firewall_manager_get_type())
-#define NM_FIREWALL_MANAGER(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FIREWALL_MANAGER, NMFirewallManager))
-#define NM_FIREWALL_MANAGER_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_FIREWALL_MANAGER, NMFirewallManagerClass))
-#define NM_IS_FIREWALL_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_FIREWALL_MANAGER))
-#define NM_IS_FIREWALL_MANAGER_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_FIREWALL_MANAGER))
-#define NM_FIREWALL_MANAGER_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_FIREWALL_MANAGER, NMFirewallManagerClass))
-
-#define NM_FIREWALL_MANAGER_STATE_CHANGED "state-changed"
-
-typedef struct _NMFirewallManagerCallId NMFirewallManagerCallId;
-
-typedef struct _NMFirewallManager      NMFirewallManager;
-typedef struct _NMFirewallManagerClass NMFirewallManagerClass;
-
-GType nm_firewall_manager_get_type(void);
-
-NMFirewallManager *nm_firewall_manager_get(void);
-
-gboolean nm_firewall_manager_get_running(NMFirewallManager *self);
-
-typedef void (*NMFirewallManagerAddRemoveCallback)(NMFirewallManager *      self,
-                                                   NMFirewallManagerCallId *call_id,
-                                                   GError *                 error,
-                                                   gpointer                 user_data);
-
-NMFirewallManagerCallId *
-nm_firewall_manager_add_or_change_zone(NMFirewallManager *                mgr,
-                                       const char *                       iface,
-                                       const char *                       zone,
-                                       gboolean                           add,
-                                       NMFirewallManagerAddRemoveCallback callback,
-                                       gpointer                           user_data);
-NMFirewallManagerCallId *
-nm_firewall_manager_remove_from_zone(NMFirewallManager *                mgr,
-                                     const char *                       iface,
-                                     const char *                       zone,
-                                     NMFirewallManagerAddRemoveCallback callback,
-                                     gpointer                           user_data);
-
-void nm_firewall_manager_cancel_call(NMFirewallManagerCallId *call_id);
-
-#endif /* __NETWORKMANAGER_FIREWALL_MANAGER_H__ */
diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c
new file mode 100644
index 00000000..3f225156
--- /dev/null
+++ b/src/core/nm-firewall-utils.c
@@ -0,0 +1,816 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2004 - 2016 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#include "src/core/nm-default-daemon.h"
+
+#include "nm-firewall-utils.h"
+
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "libnm-platform/nm-platform.h"
+
+#include "nm-config.h"
+#include "NetworkManagerUtils.h"
+
+/*****************************************************************************/
+
+static const struct {
+    const char *name;
+    const char *path;
+} FirewallBackends[] = {
+    [NM_FIREWALL_BACKEND_NONE - 1] =
+        {
+            .name = "none",
+        },
+    [NM_FIREWALL_BACKEND_NFTABLES - 1] =
+        {
+            .name = "nftables",
+            .path = NFT_PATH,
+        },
+    [NM_FIREWALL_BACKEND_IPTABLES - 1] =
+        {
+            .name = "iptables",
+            .path = IPTABLES_PATH,
+        },
+};
+
+/*****************************************************************************/
+
+#define _SHARE_IPTABLES_SUBNET_TO_STR_LEN (INET_ADDRSTRLEN + 1 + 2 + 1)
+
+static const char *
+_share_iptables_subnet_to_str(char      buf[static _SHARE_IPTABLES_SUBNET_TO_STR_LEN],
+                              in_addr_t addr,
+                              guint8    plen)
+{
+    char      buf_addr[INET_ADDRSTRLEN];
+    in_addr_t netmask;
+    int       l;
+
+    netmask = _nm_utils_ip4_prefix_to_netmask(plen);
+
+    l = g_snprintf(buf,
+                   _SHARE_IPTABLES_SUBNET_TO_STR_LEN,
+                   "%s/%u",
+                   _nm_utils_inet4_ntop(addr & netmask, buf_addr),
+                   plen);
+    nm_assert(l < _SHARE_IPTABLES_SUBNET_TO_STR_LEN);
+    return buf;
+}
+
+static char *
+_share_iptables_get_name(gboolean is_iptables_chain, const char *prefix, const char *ip_iface)
+{
+    NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE);
+    gsize    ip_iface_len;
+
+    nm_assert(prefix);
+    nm_assert(ip_iface);
+
+    /* This function is used to generate iptables chain names and comments.
+     * Chain names must be shorter than 29 chars. Comments don't have this
+     * limitation.
+     *
+     * Below we sanitize the ip_iface. If it's all benign, we use
+     * - either "-$IP_IFACE" (at most 16 chars)
+     * - otherwise, we base64 encode the name as "$(base64 $IP_IFACE)", at
+     *   most 20 chars.
+     *
+     * Since for benign names we already add a '-', prefix probably should not
+     * contain a '-'. The '-' is necessary to distinguish between base64 encoding
+     * an plain name.
+     *
+     * That means, for chain names the prefix must be at most 8 chars long. */
+    nm_assert(!is_iptables_chain || (strlen(prefix) <= 8));
+
+    nm_str_buf_append(&strbuf, prefix);
+
+    ip_iface_len = strlen(ip_iface);
+    G_STATIC_ASSERT_EXPR(NMP_IFNAMSIZ == 16);
+    if (ip_iface_len >= NMP_IFNAMSIZ) {
+        nm_assert_not_reached();
+        ip_iface_len = NMP_IFNAMSIZ - 1;
+    }
+
+    if (NM_STRCHAR_ALL(ip_iface,
+                       ch,
+                       (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')
+                           || (ch >= 'A' && ch <= 'Z') || NM_IN_SET(ch, '.', '_', '-', '+'))) {
+        nm_str_buf_append_c(&strbuf, '-');
+        nm_str_buf_append(&strbuf, ip_iface);
+    } else {
+        gs_free char *s = NULL;
+
+        s = g_base64_encode((const guchar *) ip_iface, ip_iface_len);
+        nm_str_buf_append(&strbuf, s);
+    }
+
+    return nm_str_buf_finalize(&strbuf, NULL);
+}
+
+static gboolean
+_share_iptables_call_v(const char *const *argv)
+{
+    gs_free_error GError *error    = NULL;
+    gs_free char *        argv_str = NULL;
+    int                   status;
+
+    nm_log_dbg(LOGD_SHARING, "iptables: %s", (argv_str = g_strjoinv(" ", (char **) argv)));
+
+    if (!g_spawn_sync("/",
+                      (char **) argv,
+                      (char **) NM_PTRARRAY_EMPTY(const char *),
+                      G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+                      NULL,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &status,
+                      &error)) {
+        nm_log_warn(LOGD_SHARING,
+                    "iptables: error executing command %s: %s",
+                    argv[0],
+                    error->message);
+        return FALSE;
+    }
+
+    if (!g_spawn_check_exit_status(status, &error)) {
+        nm_log_warn(LOGD_SHARING, "iptables: command %s failed: %s", argv[0], error->message);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+#define _share_iptables_call(...) _share_iptables_call_v(NM_MAKE_STRV(__VA_ARGS__))
+
+static gboolean
+_share_iptables_chain_op(const char *table, const char *chain, const char *op)
+{
+    return _share_iptables_call("" IPTABLES_PATH "", "--table", table, op, chain);
+}
+
+static gboolean
+_share_iptables_chain_delete(const char *table, const char *chain)
+{
+    _share_iptables_chain_op(table, chain, "--flush");
+    return _share_iptables_chain_op(table, chain, "--delete-chain");
+}
+
+static gboolean
+_share_iptables_chain_add(const char *table, const char *chain)
+{
+    if (_share_iptables_chain_op(table, chain, "--new-chain"))
+        return TRUE;
+
+    _share_iptables_chain_delete(table, chain);
+    return _share_iptables_chain_op(table, chain, "--new-chain");
+}
+
+static void
+_share_iptables_set_masquerade(gboolean add, const char *ip_iface, in_addr_t addr, guint8 plen)
+{
+    char          str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN];
+    gs_free char *comment_name = NULL;
+
+    comment_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface);
+
+    _share_iptables_subnet_to_str(str_subnet, addr, plen);
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "nat",
+                         add ? "--insert" : "--delete",
+                         "POSTROUTING",
+                         "--source",
+                         str_subnet,
+                         "!",
+                         "--destination",
+                         str_subnet,
+                         "--jump",
+                         "MASQUERADE",
+                         "-m",
+                         "comment",
+                         "--comment",
+                         comment_name);
+}
+
+static void
+_share_iptables_set_shared_chains_add(const char *chain_input,
+                                      const char *chain_forward,
+                                      const char *ip_iface,
+                                      in_addr_t   addr,
+                                      guint       plen)
+{
+    const char *const input_params[][2] = {
+        {
+            "tcp",
+            "67",
+        },
+        {
+            "udp",
+            "67",
+        },
+        {
+            "tcp",
+            "53",
+        },
+        {
+            "udp",
+            "53",
+        },
+    };
+    char str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN];
+    int  i;
+
+    _share_iptables_subnet_to_str(str_subnet, addr, plen);
+
+    _share_iptables_chain_add("filter", chain_input);
+
+    for (i = 0; i < (int) G_N_ELEMENTS(input_params); i++) {
+        _share_iptables_call("" IPTABLES_PATH "",
+                             "--table",
+                             "filter",
+                             "--append",
+                             chain_input,
+                             "--protocol",
+                             input_params[i][0],
+                             "--destination-port",
+                             input_params[i][1],
+                             "--jump",
+                             "ACCEPT");
+    }
+
+    _share_iptables_chain_add("filter", chain_forward);
+
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "filter",
+                         "--append",
+                         chain_forward,
+                         "--destination",
+                         str_subnet,
+                         "--out-interface",
+                         ip_iface,
+                         "--match",
+                         "state",
+                         "--state",
+                         "ESTABLISHED,RELATED",
+                         "--jump",
+                         "ACCEPT");
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "filter",
+                         "--append",
+                         chain_forward,
+                         "--source",
+                         str_subnet,
+                         "--in-interface",
+                         ip_iface,
+                         "--jump",
+                         "ACCEPT");
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "filter",
+                         "--append",
+                         chain_forward,
+                         "--in-interface",
+                         ip_iface,
+                         "--out-interface",
+                         ip_iface,
+                         "--jump",
+                         "ACCEPT");
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "filter",
+                         "--append",
+                         chain_forward,
+                         "--out-interface",
+                         ip_iface,
+                         "--jump",
+                         "REJECT");
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "filter",
+                         "--append",
+                         chain_forward,
+                         "--in-interface",
+                         ip_iface,
+                         "--jump",
+                         "REJECT");
+}
+
+static void
+_share_iptables_set_shared_chains_delete(const char *chain_input, const char *chain_forward)
+{
+    _share_iptables_chain_delete("filter", chain_input);
+    _share_iptables_chain_delete("filter", chain_forward);
+}
+
+_nm_unused static void
+_share_iptables_set_shared(gboolean add, const char *ip_iface, in_addr_t addr, guint plen)
+{
+    gs_free char *comment_name  = NULL;
+    gs_free char *chain_input   = NULL;
+    gs_free char *chain_forward = NULL;
+
+    comment_name  = _share_iptables_get_name(FALSE, "nm-shared", ip_iface);
+    chain_input   = _share_iptables_get_name(TRUE, "nm-sh-in", ip_iface);
+    chain_forward = _share_iptables_get_name(TRUE, "nm-sh-fw", ip_iface);
+
+    if (add)
+        _share_iptables_set_shared_chains_add(chain_input, chain_forward, ip_iface, addr, plen);
+
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "filter",
+                         add ? "--insert" : "--delete",
+                         "INPUT",
+                         "--in-interface",
+                         ip_iface,
+                         "--jump",
+                         chain_input,
+                         "-m",
+                         "comment",
+                         "--comment",
+                         comment_name);
+
+    _share_iptables_call("" IPTABLES_PATH "",
+                         "--table",
+                         "filter",
+                         add ? "--insert" : "--delete",
+                         "FORWARD",
+                         "--jump",
+                         chain_forward,
+                         "-m",
+                         "comment",
+                         "--comment",
+                         comment_name);
+
+    if (!add)
+        _share_iptables_set_shared_chains_delete(chain_input, chain_forward);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+    GTask *       task;
+    GSubprocess * subprocess;
+    GSource *     timeout_source;
+    GCancellable *intern_cancellable;
+    char *        identifier;
+    gulong        cancellable_id;
+} FwNftCallData;
+
+static void
+_fw_nft_call_data_free(FwNftCallData *call_data, GError *error_take)
+{
+    nm_clear_g_signal_handler(g_task_get_cancellable(call_data->task), &call_data->cancellable_id);
+    nm_clear_g_cancellable(&call_data->intern_cancellable);
+    nm_clear_g_source_inst(&call_data->timeout_source);
+
+    if (error_take)
+        g_task_return_error(call_data->task, error_take);
+    else
+        g_task_return_boolean(call_data->task, TRUE);
+
+    g_object_unref(call_data->task);
+    nm_g_object_unref(call_data->subprocess);
+    g_free(call_data->identifier);
+
+    nm_g_slice_free(call_data);
+}
+
+static void
+_fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    FwNftCallData *call_data          = user_data;
+    gs_free_error GError *error       = NULL;
+    gs_unref_bytes GBytes *stdout_buf = NULL;
+    gs_unref_bytes GBytes *stderr_buf = NULL;
+
+    nm_assert(source == (gpointer) call_data->subprocess);
+
+    if (!g_subprocess_communicate_finish(G_SUBPROCESS(source),
+                                         result,
+                                         &stdout_buf,
+                                         &stderr_buf,
+                                         &error)) {
+        /* on any error, the process might still be running. We need to abort it in
+         * the background... */
+        if (!nm_utils_error_is_cancelled(error)) {
+            nm_log_dbg(LOGD_SHARING,
+                       "firewall: nft[%s]: communication failed: %s. Kill process",
+                       call_data->identifier,
+                       error->message);
+        } else if (!call_data->timeout_source) {
+            nm_log_dbg(LOGD_SHARING,
+                       "firewall: ntf[%s]: communication timed out. Kill process",
+                       call_data->identifier);
+            nm_clear_error(&error);
+            nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "timeout communicating with nft");
+        } else {
+            nm_log_dbg(LOGD_SHARING,
+                       "firewall: ntf[%s]: communication cancelled. Kill process",
+                       call_data->identifier);
+        }
+
+        {
+            _nm_unused nm_auto_pop_gmaincontext GMainContext *main_context =
+                nm_g_main_context_push_thread_default_if_necessary(NULL);
+
+            nm_shutdown_wait_obj_register_object(call_data->subprocess, "nft-terminate");
+            G_STATIC_ASSERT_EXPR(200 < NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG * 2 / 3);
+            nm_g_subprocess_terminate_in_background(call_data->subprocess, 200);
+        }
+    } else if (g_subprocess_get_successful(call_data->subprocess)) {
+        nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: command successful", call_data->identifier);
+    } else {
+        gs_free char *ss_stdout    = NULL;
+        gs_free char *ss_stderr    = NULL;
+        gboolean      print_stdout = (stdout_buf && g_bytes_get_size(stdout_buf) > 0);
+        gboolean      print_stderr = (stderr_buf && g_bytes_get_size(stderr_buf) > 0);
+
+        nm_log_warn(LOGD_SHARING,
+                    "firewall: nft[%s]: command failed:%s%s%s%s%s%s%s",
+                    call_data->identifier,
+                    print_stdout || print_stderr ? "" : " unknown reason",
+                    NM_PRINT_FMT_QUOTED(
+                        print_stdout,
+                        " (stdout: \"",
+                        nm_utils_buf_utf8safe_escape_bytes(stdout_buf,
+                                                           NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL,
+                                                           &ss_stdout),
+                        "\")",
+                        ""),
+                    NM_PRINT_FMT_QUOTED(
+                        print_stderr,
+                        " (stderr: \"",
+                        nm_utils_buf_utf8safe_escape_bytes(stderr_buf,
+                                                           NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL,
+                                                           &ss_stderr),
+                        "\")",
+                        ""));
+    }
+
+    _fw_nft_call_data_free(call_data, g_steal_pointer(&error));
+}
+
+static void
+_fw_nft_call_cancelled_cb(GCancellable *cancellable, gpointer user_data)
+{
+    FwNftCallData *call_data = user_data;
+
+    if (call_data->cancellable_id == 0)
+        return;
+
+    nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: operation cancelled", call_data->identifier);
+
+    nm_clear_g_signal_handler(g_task_get_cancellable(call_data->task), &call_data->cancellable_id);
+    nm_clear_g_cancellable(&call_data->intern_cancellable);
+}
+
+static gboolean
+_fw_nft_call_timeout_cb(gpointer user_data)
+{
+    FwNftCallData *call_data = user_data;
+
+    nm_clear_g_source_inst(&call_data->timeout_source);
+    nm_log_dbg(LOGD_SHARING,
+               "firewall: nft[%s]: cancel operation after timeout",
+               call_data->identifier);
+
+    nm_clear_g_cancellable(&call_data->intern_cancellable);
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+_fw_nft_call(GBytes *            stdin_buf,
+             GCancellable *      cancellable,
+             GAsyncReadyCallback callback,
+             gpointer            callback_user_data)
+{
+    gs_unref_object GSubprocessLauncher *subprocess_launcher = NULL;
+    gs_free_error GError *error                              = NULL;
+    FwNftCallData *       call_data;
+
+    call_data  = g_slice_new(FwNftCallData);
+    *call_data = (FwNftCallData){
+        .task       = nm_g_task_new(NULL, cancellable, _fw_nft_call, callback, callback_user_data),
+        .subprocess = NULL,
+        .timeout_source = NULL,
+    };
+
+    if (cancellable) {
+        call_data->cancellable_id = g_cancellable_connect(cancellable,
+                                                          G_CALLBACK(_fw_nft_call_cancelled_cb),
+                                                          call_data,
+                                                          NULL);
+        if (call_data->cancellable_id == 0) {
+            nm_log_dbg(LOGD_SHARING, "firewall: nft: already cancelled");
+            nm_utils_error_set_cancelled(&error, FALSE, NULL);
+            _fw_nft_call_data_free(call_data, g_steal_pointer(&error));
+            return;
+        }
+    }
+
+    subprocess_launcher =
+        g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE
+                                  | G_SUBPROCESS_FLAGS_STDERR_PIPE);
+    g_subprocess_launcher_set_environ(subprocess_launcher, NM_STRV_EMPTY());
+
+    call_data->subprocess = g_subprocess_launcher_spawnv(subprocess_launcher,
+                                                         NM_MAKE_STRV(NFT_PATH, "-f", "-"),
+                                                         &error);
+
+    if (!call_data->subprocess) {
+        nm_log_dbg(LOGD_SHARING, "firewall: nft: spawning nft failed: %s", error->message);
+        _fw_nft_call_data_free(call_data, g_steal_pointer(&error));
+        return;
+    }
+
+    call_data->identifier = g_strdup(g_subprocess_get_identifier(call_data->subprocess));
+
+    nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: communicate with nft", call_data->identifier);
+
+    nm_shutdown_wait_obj_register_object(call_data->task, "nft-call");
+
+    call_data->intern_cancellable = g_cancellable_new(),
+
+    g_subprocess_communicate_async(call_data->subprocess,
+                                   stdin_buf,
+                                   call_data->intern_cancellable,
+                                   _fw_nft_call_communicate_cb,
+                                   call_data);
+
+    call_data->timeout_source =
+        nm_g_source_attach(nm_g_timeout_source_new((NM_SHUTDOWN_TIMEOUT_MS * 2) / 3,
+                                                   G_PRIORITY_DEFAULT,
+                                                   _fw_nft_call_timeout_cb,
+                                                   call_data,
+                                                   NULL),
+                           g_task_get_context(call_data->task));
+}
+
+static gboolean
+_fw_nft_call_finish(GAsyncResult *result, GError **error)
+{
+    g_return_val_if_fail(nm_g_task_is_valid(result, NULL, _fw_nft_call), FALSE);
+
+    return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+    GMainLoop *loop;
+    GError **  error;
+    gboolean   success;
+} FwNftCallSyncData;
+
+static void
+_fw_nft_call_sync_done(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    FwNftCallSyncData *data = user_data;
+
+    data->success = _fw_nft_call_finish(result, data->error);
+    g_main_loop_quit(data->loop);
+}
+
+static gboolean
+_fw_nft_call_sync(GBytes *stdin_buf, GError **error)
+{
+    nm_auto_pop_and_unref_gmaincontext GMainContext *main_context =
+        nm_g_main_context_push_thread_default(g_main_context_new());
+    nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new(main_context, FALSE);
+    FwNftCallSyncData                  data      = (FwNftCallSyncData){
+        .loop  = main_loop,
+        .error = error,
+    };
+
+    _fw_nft_call(stdin_buf, NULL, _fw_nft_call_sync_done, &data);
+
+    g_main_loop_run(main_loop);
+    return data.success;
+}
+
+/*****************************************************************************/
+
+static void
+_fw_nft_set(gboolean add, const char *ip_iface, in_addr_t addr, guint8 plen)
+{
+    nm_auto_str_buf NMStrBuf strbuf   = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
+    gs_unref_bytes GBytes *stdin_buf  = NULL;
+    gs_free char *         table_name = NULL;
+    gs_free char *         ss1        = NULL;
+    char                   str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN];
+
+    table_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface);
+
+    _share_iptables_subnet_to_str(str_subnet, addr, plen);
+
+#define _append(p_strbuf, fmt, ...) nm_str_buf_append_printf((p_strbuf), "" fmt "\n", ##__VA_ARGS__)
+
+    _append(&strbuf, "add table inet %s", table_name);
+    _append(&strbuf, "%s table inet %s", add ? "flush" : "delete", table_name);
+
+    if (add) {
+        _append(&strbuf,
+                "add chain inet %s nat_postrouting {"
+                " type nat hook postrouting priority 100; policy accept; "
+                "};",
+                table_name);
+        _append(&strbuf,
+                "add rule inet %s nat_postrouting ip saddr %s ip daddr != %s masquerade;",
+                table_name,
+                str_subnet,
+                str_subnet);
+
+        /* This filter_input chain serves no real purpose, because "accept" only stops
+         * evaluation of the current rule. It cannot fully accept the packet. Since
+         * this chain has no other rules, it is useless in this form.
+         */
+        /*
+        _append(&strbuf,
+                "add chain inet %s filter_input {"
+                " type filter hook input priority 0; policy accept; "
+                "};",
+                table_name);
+        _append(&strbuf, "add rule inet %s filter_input tcp dport { 67, 53 } accept;", table_name);
+        _append(&strbuf, "add rule inet %s filter_input udp dport { 67, 53 } accept;", table_name);
+        */
+
+        _append(&strbuf,
+                "add chain inet %s filter_forward {"
+                " type filter hook forward priority 0; policy accept; "
+                "};",
+                table_name);
+        _append(&strbuf,
+                "add rule inet %s filter_forward ip daddr %s oifname \"%s\" "
+                " ct state { established, related } accept;",
+                table_name,
+                str_subnet,
+                ip_iface);
+        _append(&strbuf,
+                "add rule inet %s filter_forward ip saddr %s iifname \"%s\" accept;",
+                table_name,
+                str_subnet,
+                ip_iface);
+        _append(&strbuf,
+                "add rule inet %s filter_forward iifname \"%s\" oifname \"%s\" accept;",
+                table_name,
+                ip_iface,
+                ip_iface);
+        _append(&strbuf,
+                "add rule inet %s filter_forward iifname \"%s\" reject;",
+                table_name,
+                ip_iface);
+        _append(&strbuf,
+                "add rule inet %s filter_forward oifname \"%s\" reject;",
+                table_name,
+                ip_iface);
+    }
+
+    nm_log_trace(LOGD_SHARING,
+                 "firewall: nft command: [ %s ]",
+                 nm_utils_str_utf8safe_escape(nm_str_buf_get_str(&strbuf),
+                                              NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL,
+                                              &ss1));
+
+    stdin_buf = g_bytes_new_static(nm_str_buf_get_str(&strbuf), strbuf.len);
+
+    _fw_nft_call_sync(stdin_buf, NULL);
+}
+
+/*****************************************************************************/
+
+struct _NMFirewallConfig {
+    char *    ip_iface;
+    in_addr_t addr;
+    guint8    plen;
+};
+
+NMFirewallConfig *
+nm_firewall_config_new(const char *ip_iface, in_addr_t addr, guint8 plen)
+{
+    NMFirewallConfig *self;
+
+    nm_assert(ip_iface);
+    nm_assert(addr != 0u);
+    nm_assert(plen <= 32);
+
+    self  = g_slice_new(NMFirewallConfig);
+    *self = (NMFirewallConfig){
+        .ip_iface = g_strdup(ip_iface),
+        .addr     = addr,
+        .plen     = plen,
+    };
+    return self;
+}
+
+void
+nm_firewall_config_free(NMFirewallConfig *self)
+{
+    if (!self)
+        return;
+
+    g_free(self->ip_iface);
+    nm_g_slice_free(self);
+}
+
+void
+nm_firewall_config_apply(NMFirewallConfig *self, gboolean shared)
+{
+    switch (nm_firewall_utils_get_backend()) {
+    case NM_FIREWALL_BACKEND_IPTABLES:
+        _share_iptables_set_masquerade(shared, self->ip_iface, self->addr, self->plen);
+        _share_iptables_set_shared(shared, self->ip_iface, self->addr, self->plen);
+        break;
+    case NM_FIREWALL_BACKEND_NFTABLES:
+        _fw_nft_set(shared, self->ip_iface, self->addr, self->plen);
+        break;
+    case NM_FIREWALL_BACKEND_NONE:
+        break;
+    default:
+        nm_assert_not_reached();
+        break;
+    }
+}
+
+/*****************************************************************************/
+
+static NMFirewallBackend
+_firewall_backend_detect(void)
+{
+    if (g_file_test(NFT_PATH, G_FILE_TEST_IS_EXECUTABLE))
+        return NM_FIREWALL_BACKEND_NFTABLES;
+    if (g_file_test(IPTABLES_PATH, G_FILE_TEST_IS_EXECUTABLE))
+        return NM_FIREWALL_BACKEND_IPTABLES;
+
+    return NM_FIREWALL_BACKEND_NFTABLES;
+}
+
+NMFirewallBackend
+nm_firewall_utils_get_backend(void)
+{
+    static int backend = NM_FIREWALL_BACKEND_UNKNOWN;
+    int        b;
+
+again:
+    b = g_atomic_int_get(&backend);
+    if (b == NM_FIREWALL_BACKEND_UNKNOWN) {
+        gs_free char *conf_value = NULL;
+        gboolean      detect;
+        int           i;
+
+        conf_value =
+            nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG,
+                                     NM_CONFIG_KEYFILE_GROUP_MAIN,
+                                     NM_CONFIG_KEYFILE_KEY_MAIN_FIREWALL_BACKEND,
+                                     NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
+
+        if (conf_value) {
+            for (i = 0; i < (int) G_N_ELEMENTS(FirewallBackends); i++) {
+                if (!g_ascii_strcasecmp(conf_value, FirewallBackends[i].name)) {
+                    b = (i + 1);
+                    break;
+                }
+            }
+        }
+
+        detect = (b == NM_FIREWALL_BACKEND_UNKNOWN);
+        if (detect)
+            b = _firewall_backend_detect();
+
+        nm_assert(NM_IN_SET(b,
+                            NM_FIREWALL_BACKEND_NONE,
+                            NM_FIREWALL_BACKEND_IPTABLES,
+                            NM_FIREWALL_BACKEND_NFTABLES));
+
+        if (!g_atomic_int_compare_and_exchange(&backend, NM_FIREWALL_BACKEND_UNKNOWN, b))
+            goto again;
+
+        nm_log_dbg(LOGD_SHARING,
+                   "firewall: use %s backend%s%s%s%s%s%s%s",
+                   FirewallBackends[b - 1].name,
+                   NM_PRINT_FMT_QUOTED(FirewallBackends[b - 1].path,
+                                       " (",
+                                       FirewallBackends[b - 1].path,
+                                       ")",
+                                       ""),
+                   detect ? " (detected)" : "",
+                   NM_PRINT_FMT_QUOTED(detect && conf_value,
+                                       " (invalid setting \"",
+                                       conf_value,
+                                       "\")",
+                                       ""));
+    }
+
+    nm_assert(NM_IN_SET(b,
+                        NM_FIREWALL_BACKEND_NONE,
+                        NM_FIREWALL_BACKEND_IPTABLES,
+                        NM_FIREWALL_BACKEND_NFTABLES));
+    return b;
+}
diff --git a/src/core/nm-firewall-utils.h b/src/core/nm-firewall-utils.h
new file mode 100644
index 00000000..3d6c8a69
--- /dev/null
+++ b/src/core/nm-firewall-utils.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2004 - 2016 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#ifndef __NM_FIREWALL_UTILS_H__
+#define __NM_FIREWALL_UTILS_H__
+
+typedef enum {
+    NM_FIREWALL_BACKEND_UNKNOWN,
+    NM_FIREWALL_BACKEND_NONE,
+    NM_FIREWALL_BACKEND_IPTABLES,
+    NM_FIREWALL_BACKEND_NFTABLES,
+} NMFirewallBackend;
+
+NMFirewallBackend nm_firewall_utils_get_backend(void);
+
+/*****************************************************************************/
+
+typedef struct _NMFirewallConfig NMFirewallConfig;
+
+NMFirewallConfig *nm_firewall_config_new(const char *ip_iface, in_addr_t addr, guint8 plen);
+
+void nm_firewall_config_free(NMFirewallConfig *self);
+
+void nm_firewall_config_apply(NMFirewallConfig *self, gboolean shared);
+
+#endif /* __NM_FIREWALL_UTILS_H__ */
diff --git a/src/core/nm-firewall-manager.c b/src/core/nm-firewalld-manager.c
index 8f476e51..c562d978 100644
--- a/src/core/nm-firewall-manager.c
+++ b/src/core/nm-firewalld-manager.c
@@ -5,9 +5,9 @@
 
 #include "src/core/nm-default-daemon.h"
 
-#include "nm-firewall-manager.h"
+#include "nm-firewalld-manager.h"
 
-#include "nm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
 #include "c-list/src/c-list.h"
 
 #include "NetworkManagerUtils.h"
@@ -34,25 +34,25 @@ typedef struct {
 
     bool dbus_inited : 1;
     bool running : 1;
-} NMFirewallManagerPrivate;
+} NMFirewalldManagerPrivate;
 
-struct _NMFirewallManager {
-    GObject                  parent;
-    NMFirewallManagerPrivate _priv;
+struct _NMFirewalldManager {
+    GObject                   parent;
+    NMFirewalldManagerPrivate _priv;
 };
 
-struct _NMFirewallManagerClass {
+struct _NMFirewalldManagerClass {
     GObjectClass parent;
 };
 
-G_DEFINE_TYPE(NMFirewallManager, nm_firewall_manager, G_TYPE_OBJECT)
+G_DEFINE_TYPE(NMFirewalldManager, nm_firewalld_manager, G_TYPE_OBJECT)
 
-#define NM_FIREWALL_MANAGER_GET_PRIVATE(self) \
-    _NM_GET_PRIVATE(self, NMFirewallManager, NM_IS_FIREWALL_MANAGER)
+#define NM_FIREWALLD_MANAGER_GET_PRIVATE(self) \
+    _NM_GET_PRIVATE(self, NMFirewalldManager, NM_IS_FIREWALLD_MANAGER)
 
 /*****************************************************************************/
 
-NM_DEFINE_SINGLETON_GETTER(NMFirewallManager, nm_firewall_manager_get, NM_TYPE_FIREWALL_MANAGER);
+NM_DEFINE_SINGLETON_GETTER(NMFirewalldManager, nm_firewalld_manager_get, NM_TYPE_FIREWALLD_MANAGER);
 
 /*****************************************************************************/
 
@@ -62,15 +62,15 @@ typedef enum {
     OPS_TYPE_REMOVE,
 } OpsType;
 
-struct _NMFirewallManagerCallId {
+struct _NMFirewalldManagerCallId {
     CList lst;
 
-    NMFirewallManager *self;
+    NMFirewalldManager *self;
 
     char *iface;
 
-    NMFirewallManagerAddRemoveCallback callback;
-    gpointer                           user_data;
+    NMFirewalldManagerAddRemoveCallback callback;
+    gpointer                            user_data;
 
     union {
         struct {
@@ -110,9 +110,9 @@ _ops_type_to_string(OpsType ops_type)
     G_STMT_START                                                                    \
     {                                                                               \
         if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) {                         \
-            NMFirewallManagerCallId *_call_id = (call_id);                          \
-            char                     _prefix_name[30];                              \
-            char                     _prefix_info[100];                             \
+            NMFirewalldManagerCallId *_call_id = (call_id);                         \
+            char                      _prefix_name[30];                             \
+            char                      _prefix_info[100];                            \
                                                                                     \
             _nm_log((level),                                                        \
                     (_NMLOG_DOMAIN),                                                \
@@ -147,7 +147,7 @@ _ops_type_to_string(OpsType ops_type)
 /*****************************************************************************/
 
 static gboolean
-_get_running(NMFirewallManagerPrivate *priv)
+_get_running(NMFirewalldManagerPrivate *priv)
 {
     /* when starting, we need to asynchronously check whether there is
      * a name owner. During that time we optimistically assume that the
@@ -158,27 +158,27 @@ _get_running(NMFirewallManagerPrivate *priv)
 }
 
 gboolean
-nm_firewall_manager_get_running(NMFirewallManager *self)
+nm_firewalld_manager_get_running(NMFirewalldManager *self)
 {
-    g_return_val_if_fail(NM_IS_FIREWALL_MANAGER(self), FALSE);
+    g_return_val_if_fail(NM_IS_FIREWALLD_MANAGER(self), FALSE);
 
-    return _get_running(NM_FIREWALL_MANAGER_GET_PRIVATE(self));
+    return _get_running(NM_FIREWALLD_MANAGER_GET_PRIVATE(self));
 }
 
 /*****************************************************************************/
 
-static NMFirewallManagerCallId *
-_cb_info_create(NMFirewallManager *                self,
-                OpsType                            ops_type,
-                const char *                       iface,
-                const char *                       zone,
-                NMFirewallManagerAddRemoveCallback callback,
-                gpointer                           user_data)
+static NMFirewalldManagerCallId *
+_cb_info_create(NMFirewalldManager *                self,
+                OpsType                             ops_type,
+                const char *                        iface,
+                const char *                        zone,
+                NMFirewalldManagerAddRemoveCallback callback,
+                gpointer                            user_data)
 {
-    NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
-    NMFirewallManagerCallId * call_id;
+    NMFirewalldManagerPrivate *priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
+    NMFirewalldManagerCallId * call_id;
 
-    call_id = g_slice_new0(NMFirewallManagerCallId);
+    call_id = g_slice_new0(NMFirewalldManagerCallId);
 
     call_id->self      = g_object_ref(self);
     call_id->ops_type  = ops_type;
@@ -198,7 +198,7 @@ _cb_info_create(NMFirewallManager *                self,
 }
 
 static void
-_cb_info_complete(NMFirewallManagerCallId *call_id, GError *error)
+_cb_info_complete(NMFirewalldManagerCallId *call_id, GError *error)
 {
     c_list_unlink(&call_id->lst);
 
@@ -219,13 +219,13 @@ _cb_info_complete(NMFirewallManagerCallId *call_id, GError *error)
 static gboolean
 _handle_idle_cb(gpointer user_data)
 {
-    NMFirewallManager *      self;
-    NMFirewallManagerCallId *call_id = user_data;
+    NMFirewalldManager *      self;
+    NMFirewalldManagerCallId *call_id = user_data;
 
     nm_assert(call_id);
-    nm_assert(NM_IS_FIREWALL_MANAGER(call_id->self));
+    nm_assert(NM_IS_FIREWALLD_MANAGER(call_id->self));
     nm_assert(call_id->is_idle);
-    nm_assert(c_list_contains(&NM_FIREWALL_MANAGER_GET_PRIVATE(call_id->self)->pending_calls,
+    nm_assert(c_list_contains(&NM_FIREWALLD_MANAGER_GET_PRIVATE(call_id->self)->pending_calls,
                               &call_id->lst));
 
     self = call_id->self;
@@ -239,7 +239,7 @@ _handle_idle_cb(gpointer user_data)
 }
 
 static gboolean
-_handle_idle_start(NMFirewallManager *self, NMFirewallManagerCallId *call_id)
+_handle_idle_start(NMFirewalldManager *self, NMFirewalldManagerCallId *call_id)
 {
     if (!call_id->callback) {
         /* if the user did not provide a callback and firewalld is not running,
@@ -256,8 +256,8 @@ _handle_idle_start(NMFirewallManager *self, NMFirewallManagerCallId *call_id)
 static void
 _handle_dbus_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
-    NMFirewallManager *      self;
-    NMFirewallManagerCallId *call_id;
+    NMFirewalldManager *      self;
+    NMFirewalldManagerCallId *call_id;
     gs_free_error GError *error    = NULL;
     gs_unref_variant GVariant *ret = NULL;
 
@@ -269,9 +269,9 @@ _handle_dbus_cb(GObject *source, GAsyncResult *result, gpointer user_data)
     call_id = user_data;
 
     nm_assert(call_id);
-    nm_assert(NM_IS_FIREWALL_MANAGER(call_id->self));
+    nm_assert(NM_IS_FIREWALLD_MANAGER(call_id->self));
     nm_assert(!call_id->is_idle);
-    nm_assert(c_list_contains(&NM_FIREWALL_MANAGER_GET_PRIVATE(call_id->self)->pending_calls,
+    nm_assert(c_list_contains(&NM_FIREWALLD_MANAGER_GET_PRIVATE(call_id->self)->pending_calls,
                               &call_id->lst));
 
     self = call_id->self;
@@ -308,11 +308,11 @@ _handle_dbus_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 }
 
 static void
-_handle_dbus_start(NMFirewallManager *self, NMFirewallManagerCallId *call_id)
+_handle_dbus_start(NMFirewalldManager *self, NMFirewalldManagerCallId *call_id)
 {
-    NMFirewallManagerPrivate *priv        = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
-    const char *              dbus_method = NULL;
-    GVariant *                arg;
+    NMFirewalldManagerPrivate *priv        = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
+    const char *               dbus_method = NULL;
+    GVariant *                 arg;
 
     nm_assert(call_id);
     nm_assert(priv->running);
@@ -324,7 +324,7 @@ _handle_dbus_start(NMFirewallManager *self, NMFirewallManagerCallId *call_id)
         dbus_method = "addInterface";
         break;
     case OPS_TYPE_CHANGE:
-        dbus_method = "changeZone";
+        dbus_method = "changeZoneOfInterface";
         break;
     case OPS_TYPE_REMOVE:
         dbus_method = "removeInterface";
@@ -354,21 +354,21 @@ _handle_dbus_start(NMFirewallManager *self, NMFirewallManagerCallId *call_id)
                            call_id);
 }
 
-static NMFirewallManagerCallId *
-_start_request(NMFirewallManager *                self,
-               OpsType                            ops_type,
-               const char *                       iface,
-               const char *                       zone,
-               NMFirewallManagerAddRemoveCallback callback,
-               gpointer                           user_data)
+static NMFirewalldManagerCallId *
+_start_request(NMFirewalldManager *                self,
+               OpsType                             ops_type,
+               const char *                        iface,
+               const char *                        zone,
+               NMFirewalldManagerAddRemoveCallback callback,
+               gpointer                            user_data)
 {
-    NMFirewallManagerPrivate *priv;
-    NMFirewallManagerCallId * call_id;
+    NMFirewalldManagerPrivate *priv;
+    NMFirewalldManagerCallId * call_id;
 
-    g_return_val_if_fail(NM_IS_FIREWALL_MANAGER(self), NULL);
+    g_return_val_if_fail(NM_IS_FIREWALLD_MANAGER(self), NULL);
     g_return_val_if_fail(iface && *iface, NULL);
 
-    priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
+    priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
 
     call_id = _cb_info_create(self, ops_type, iface, zone, callback, user_data);
 
@@ -404,13 +404,13 @@ _start_request(NMFirewallManager *                self,
     return call_id;
 }
 
-NMFirewallManagerCallId *
-nm_firewall_manager_add_or_change_zone(NMFirewallManager *self,
-                                       const char *       iface,
-                                       const char *       zone,
-                                       gboolean           add, /* TRUE == add, FALSE == change */
-                                       NMFirewallManagerAddRemoveCallback callback,
-                                       gpointer                           user_data)
+NMFirewalldManagerCallId *
+nm_firewalld_manager_add_or_change_zone(NMFirewalldManager *self,
+                                        const char *        iface,
+                                        const char *        zone,
+                                        gboolean            add, /* TRUE == add, FALSE == change */
+                                        NMFirewalldManagerAddRemoveCallback callback,
+                                        gpointer                            user_data)
 {
     return _start_request(self,
                           add ? OPS_TYPE_ADD : OPS_TYPE_CHANGE,
@@ -420,33 +420,33 @@ nm_firewall_manager_add_or_change_zone(NMFirewallManager *self,
                           user_data);
 }
 
-NMFirewallManagerCallId *
-nm_firewall_manager_remove_from_zone(NMFirewallManager *                self,
-                                     const char *                       iface,
-                                     const char *                       zone,
-                                     NMFirewallManagerAddRemoveCallback callback,
-                                     gpointer                           user_data)
+NMFirewalldManagerCallId *
+nm_firewalld_manager_remove_from_zone(NMFirewalldManager *                self,
+                                      const char *                        iface,
+                                      const char *                        zone,
+                                      NMFirewalldManagerAddRemoveCallback callback,
+                                      gpointer                            user_data)
 {
     return _start_request(self, OPS_TYPE_REMOVE, iface, zone, callback, user_data);
 }
 
 void
-nm_firewall_manager_cancel_call(NMFirewallManagerCallId *call_id)
+nm_firewalld_manager_cancel_call(NMFirewalldManagerCallId *call_id)
 {
-    NMFirewallManager *       self;
-    NMFirewallManagerPrivate *priv;
+    NMFirewalldManager *       self;
+    NMFirewalldManagerPrivate *priv;
     gs_free_error GError *error = NULL;
 
     g_return_if_fail(call_id);
-    g_return_if_fail(NM_IS_FIREWALL_MANAGER(call_id->self));
+    g_return_if_fail(NM_IS_FIREWALLD_MANAGER(call_id->self));
     g_return_if_fail(!c_list_is_empty(&call_id->lst));
 
     self = call_id->self;
-    priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
+    priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
 
     nm_assert(c_list_contains(&priv->pending_calls, &call_id->lst));
 
-    nm_utils_error_set_cancelled(&error, FALSE, "NMFirewallManager");
+    nm_utils_error_set_cancelled(&error, FALSE, "NMFirewalldManager");
 
     _LOGD(call_id, "complete: cancel (%s)", error->message);
 
@@ -456,13 +456,13 @@ nm_firewall_manager_cancel_call(NMFirewallManagerCallId *call_id)
 /*****************************************************************************/
 
 static void
-name_owner_changed(NMFirewallManager *self, const char *owner)
+name_owner_changed(NMFirewalldManager *self, const char *owner)
 {
-    _nm_unused gs_unref_object NMFirewallManager *self_keep_alive = g_object_ref(self);
-    NMFirewallManagerPrivate *                    priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
-    gboolean                                      was_running;
-    gboolean                                      now_running;
-    gboolean                                      just_initied;
+    _nm_unused gs_unref_object NMFirewalldManager *self_keep_alive = g_object_ref(self);
+    NMFirewalldManagerPrivate *                    priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
+    gboolean                                       was_running;
+    gboolean                                       now_running;
+    gboolean                                       just_initied;
 
     owner = nm_str_not_empty(owner);
 
@@ -480,8 +480,8 @@ name_owner_changed(NMFirewallManager *self, const char *owner)
     now_running = _get_running(priv);
 
     if (just_initied) {
-        NMFirewallManagerCallId *call_id_safe;
-        NMFirewallManagerCallId *call_id;
+        NMFirewalldManagerCallId *call_id_safe;
+        NMFirewalldManagerCallId *call_id;
 
         /* We kick of the requests that we have pending. Note that this is
          * entirely asynchronous and also we don't invoke any callbacks for
@@ -524,8 +524,8 @@ name_owner_changed_cb(GDBusConnection *connection,
                       GVariant *       parameters,
                       gpointer         user_data)
 {
-    NMFirewallManager *self = user_data;
-    const char *       new_owner;
+    NMFirewalldManager *self = user_data;
+    const char *        new_owner;
 
     if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)")))
         return;
@@ -538,14 +538,14 @@ name_owner_changed_cb(GDBusConnection *connection,
 static void
 get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data)
 {
-    NMFirewallManager *       self;
-    NMFirewallManagerPrivate *priv;
+    NMFirewalldManager *       self;
+    NMFirewalldManagerPrivate *priv;
 
     if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
         return;
 
     self = user_data;
-    priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
+    priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
 
     g_clear_object(&priv->get_name_owner_cancellable);
 
@@ -555,9 +555,9 @@ get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data)
 /*****************************************************************************/
 
 static void
-nm_firewall_manager_init(NMFirewallManager *self)
+nm_firewalld_manager_init(NMFirewalldManager *self)
 {
-    NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
+    NMFirewalldManagerPrivate *priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
 
     c_list_init(&priv->pending_calls);
 
@@ -587,8 +587,8 @@ nm_firewall_manager_init(NMFirewallManager *self)
 static void
 dispose(GObject *object)
 {
-    NMFirewallManager *       self = NM_FIREWALL_MANAGER(object);
-    NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self);
+    NMFirewalldManager *       self = NM_FIREWALLD_MANAGER(object);
+    NMFirewalldManagerPrivate *priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self);
 
     /* as every pending operation takes a reference to the manager,
      * we don't expect pending operations at this point. */
@@ -598,19 +598,19 @@ dispose(GObject *object)
 
     nm_clear_g_cancellable(&priv->get_name_owner_cancellable);
 
-    G_OBJECT_CLASS(nm_firewall_manager_parent_class)->dispose(object);
+    G_OBJECT_CLASS(nm_firewalld_manager_parent_class)->dispose(object);
 
     g_clear_object(&priv->dbus_connection);
 }
 
 static void
-nm_firewall_manager_class_init(NMFirewallManagerClass *klass)
+nm_firewalld_manager_class_init(NMFirewalldManagerClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS(klass);
 
     object_class->dispose = dispose;
 
-    signals[STATE_CHANGED] = g_signal_new(NM_FIREWALL_MANAGER_STATE_CHANGED,
+    signals[STATE_CHANGED] = g_signal_new(NM_FIREWALLD_MANAGER_STATE_CHANGED,
                                           G_OBJECT_CLASS_TYPE(object_class),
                                           G_SIGNAL_RUN_FIRST,
                                           0,
diff --git a/src/core/nm-firewalld-manager.h b/src/core/nm-firewalld-manager.h
new file mode 100644
index 00000000..febb9bac
--- /dev/null
+++ b/src/core/nm-firewalld-manager.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ */
+
+#ifndef __NM_FIREWALLD_MANAGER_H__
+#define __NM_FIREWALLD_MANAGER_H__
+
+#define NM_TYPE_FIREWALLD_MANAGER (nm_firewalld_manager_get_type())
+#define NM_FIREWALLD_MANAGER(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FIREWALLD_MANAGER, NMFirewalldManager))
+#define NM_FIREWALLD_MANAGER_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_FIREWALLD_MANAGER, NMFirewalldManagerClass))
+#define NM_IS_FIREWALLD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_FIREWALLD_MANAGER))
+#define NM_IS_FIREWALLD_MANAGER_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_FIREWALLD_MANAGER))
+#define NM_FIREWALLD_MANAGER_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_FIREWALLD_MANAGER, NMFirewalldManagerClass))
+
+#define NM_FIREWALLD_MANAGER_STATE_CHANGED "state-changed"
+
+typedef struct _NMFirewalldManagerCallId NMFirewalldManagerCallId;
+
+typedef struct _NMFirewalldManager      NMFirewalldManager;
+typedef struct _NMFirewalldManagerClass NMFirewalldManagerClass;
+
+GType nm_firewalld_manager_get_type(void);
+
+NMFirewalldManager *nm_firewalld_manager_get(void);
+
+gboolean nm_firewalld_manager_get_running(NMFirewalldManager *self);
+
+typedef void (*NMFirewalldManagerAddRemoveCallback)(NMFirewalldManager *      self,
+                                                    NMFirewalldManagerCallId *call_id,
+                                                    GError *                  error,
+                                                    gpointer                  user_data);
+
+NMFirewalldManagerCallId *
+nm_firewalld_manager_add_or_change_zone(NMFirewalldManager *                mgr,
+                                        const char *                        iface,
+                                        const char *                        zone,
+                                        gboolean                            add,
+                                        NMFirewalldManagerAddRemoveCallback callback,
+                                        gpointer                            user_data);
+NMFirewalldManagerCallId *
+nm_firewalld_manager_remove_from_zone(NMFirewalldManager *                mgr,
+                                      const char *                        iface,
+                                      const char *                        zone,
+                                      NMFirewalldManagerAddRemoveCallback callback,
+                                      gpointer                            user_data);
+
+void nm_firewalld_manager_cancel_call(NMFirewalldManagerCallId *call_id);
+
+#endif /* __NM_FIREWALLD_MANAGER_H__ */
diff --git a/src/core/nm-hostname-manager.c b/src/core/nm-hostname-manager.c
index c99c8326..91e9baaa 100644
--- a/src/core/nm-hostname-manager.c
+++ b/src/core/nm-hostname-manager.c
@@ -14,11 +14,11 @@
     #include <selinux/label.h>
 #endif
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "NetworkManagerUtils.h"
 
@@ -182,7 +182,7 @@ nm_hostname_manager_read_hostname(NMHostnameManager *self)
 #if defined(HOSTNAME_PERSIST_GENTOO)
     hostname = read_hostname_gentoo(HOSTNAME_FILE);
 #elif defined(HOSTNAME_PERSIST_SLACKWARE)
-    hostname = read_hostname_slackware(HOSTNAME_FILE);
+    hostname     = read_hostname_slackware(HOSTNAME_FILE);
 #else
     if (g_file_get_contents(HOSTNAME_FILE, &hostname, NULL, NULL))
         g_strchomp(hostname);
diff --git a/src/core/nm-iface-helper.c b/src/core/nm-iface-helper.c
index b6e9981d..296bf091 100644
--- a/src/core/nm-iface-helper.c
+++ b/src/core/nm-iface-helper.c
@@ -16,19 +16,19 @@
 #include <signal.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-c-list.h"
 
 #include "main-utils.h"
 #include "NetworkManagerUtils.h"
-#include "platform/nm-linux-platform.h"
-#include "nm-platform/nm-platform-utils.h"
+#include "libnm-platform/nm-linux-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
 #include "dhcp/nm-dhcp-manager.h"
 #include "ndisc/nm-ndisc.h"
 #include "ndisc/nm-lndp-ndisc.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-setting-ip6-config.h"
-#include "systemd/nm-sd.h"
+#include "libnm-systemd-core/nm-sd.h"
 
 #if !defined(NM_DIST_VERSION)
     #define NM_DIST_VERSION VERSION
@@ -668,6 +668,7 @@ main(int argc, char *argv[])
                                                  global_opt.uuid,
                                                  RT_TABLE_MAIN,
                                                  global_opt.priority_v4,
+                                                 NM_DHCP_CLIENT_FLAGS_NONE,
                                                  !!global_opt.dhcp4_hostname,
                                                  global_opt.dhcp4_hostname,
                                                  global_opt.dhcp4_fqdn,
diff --git a/src/core/nm-ip4-config.c b/src/core/nm-ip4-config.c
index c49dfb4c..47f0ee3a 100644
--- a/src/core/nm-ip4-config.c
+++ b/src/core/nm-ip4-config.c
@@ -12,14 +12,14 @@
 #include <resolv.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
 
 #include "nm-utils.h"
-#include "platform/nmp-object.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nm-platform-utils.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-object.h"
 
 /*****************************************************************************/
@@ -649,21 +649,6 @@ nm_ip4_config_add_dependent_routes(NMIP4Config *self,
         if (my_addr->external)
             continue;
 
-        /* Pre-generate local route added by kernel */
-        r                   = nmp_object_new(NMP_OBJECT_TYPE_IP4_ROUTE, NULL);
-        route               = NMP_OBJECT_CAST_IP4_ROUTE(r);
-        route->ifindex      = ifindex;
-        route->rt_source    = NM_IP_CONFIG_SOURCE_KERNEL;
-        route->network      = my_addr->address;
-        route->plen         = 32;
-        route->pref_src     = my_addr->address;
-        route->type_coerced = nm_platform_route_type_coerce(RTN_LOCAL);
-        route->scope_inv    = nm_platform_route_scope_inv(RT_SCOPE_HOST);
-        route->table_coerced =
-            nm_platform_route_table_coerce(is_vrf ? route_table : RT_TABLE_LOCAL);
-        _add_route(self, r, NULL, NULL);
-        nm_clear_pointer(&r, nmp_object_unref);
-
         if (nm_utils_ip4_address_is_zeronet(network)) {
             /* Kernel doesn't add device-routes for destinations that
              * start with 0.x.y.z. Skip them. */
@@ -1992,6 +1977,26 @@ nm_ip_config_dump(const NMIPConfig *self, const char *detail, NMLogLevel level,
 
 /*****************************************************************************/
 
+gconstpointer
+nm_ip_config_find_first_address(const NMIPConfig *self, NMPlatformMatchFlags match_flag)
+{
+    NMDedupMultiIter           iter;
+    const NMPlatformIPAddress *address;
+
+    g_return_val_if_fail(NM_IS_IP_CONFIG(self), NULL);
+
+    nm_assert(!NM_FLAGS_ANY(
+        match_flag,
+        ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)));
+
+    nm_ip_config_iter_ip_address_for_each (&iter, self, &address) {
+        if (nm_platform_ip_address_match(nm_ip_config_get_addr_family(self), address, match_flag))
+            return address;
+    }
+
+    return NULL;
+}
+
 void
 nm_ip4_config_reset_addresses(NMIP4Config *self)
 {
@@ -3031,11 +3036,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_take_variant(value, g_variant_builder_end(&builder_data));
         break;
     case PROP_NAMESERVERS:
-        g_value_take_variant(value,
-                             g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                       priv->nameservers->data,
-                                                       priv->nameservers->len,
-                                                       sizeof(guint32)));
+        g_value_take_variant(
+            value,
+            nm_g_variant_new_au((const guint32 *) priv->nameservers->data, priv->nameservers->len));
         break;
     case PROP_DOMAINS:
         nm_utils_g_value_set_strv(value, priv->domains);
@@ -3060,11 +3063,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_take_variant(value, g_variant_builder_end(&builder_data));
         break;
     case PROP_WINS_SERVERS:
-        g_value_take_variant(value,
-                             g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                       priv->wins->data,
-                                                       priv->wins->len,
-                                                       sizeof(guint32)));
+        g_value_take_variant(
+            value,
+            nm_g_variant_new_au((const guint32 *) priv->wins->data, priv->wins->len));
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -3162,44 +3163,40 @@ finalize(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_ip4_config = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_IP4_CONFIG,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Addresses",
-                                                             "aau",
-                                                             NM_IP4_CONFIG_ADDRESSES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("AddressData",
-                                                             "aa{sv}",
-                                                             NM_IP4_CONFIG_ADDRESS_DATA),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Gateway", "s", NM_IP4_CONFIG_GATEWAY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Routes", "aau", NM_IP4_CONFIG_ROUTES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RouteData",
-                                                             "aa{sv}",
-                                                             NM_IP4_CONFIG_ROUTE_DATA),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Addresses",
+                                                           "aau",
+                                                           NM_IP4_CONFIG_ADDRESSES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData",
+                                                           "aa{sv}",
+                                                           NM_IP4_CONFIG_ADDRESS_DATA),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP4_CONFIG_GATEWAY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Routes", "aau", NM_IP4_CONFIG_ROUTES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RouteData",
+                                                           "aa{sv}",
+                                                           NM_IP4_CONFIG_ROUTE_DATA),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NameserverData",
                                                            "aa{sv}",
                                                            NM_IP4_CONFIG_NAMESERVER_DATA),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Nameservers",
-                                                             "au",
-                                                             NM_IP4_CONFIG_NAMESERVERS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Domains",
-                                                             "as",
-                                                             NM_IP4_CONFIG_DOMAINS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Searches",
-                                                             "as",
-                                                             NM_IP4_CONFIG_SEARCHES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("DnsOptions",
-                                                             "as",
-                                                             NM_IP4_CONFIG_DNS_OPTIONS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("DnsPriority",
-                                                             "i",
-                                                             NM_IP4_CONFIG_DNS_PRIORITY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Nameservers",
+                                                           "au",
+                                                           NM_IP4_CONFIG_NAMESERVERS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Domains", "as", NM_IP4_CONFIG_DOMAINS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Searches",
+                                                           "as",
+                                                           NM_IP4_CONFIG_SEARCHES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsOptions",
+                                                           "as",
+                                                           NM_IP4_CONFIG_DNS_OPTIONS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsPriority",
+                                                           "i",
+                                                           NM_IP4_CONFIG_DNS_PRIORITY),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServerData",
                                                            "as",
                                                            NM_IP4_CONFIG_WINS_SERVER_DATA),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WinsServers",
-                                                             "au",
-                                                             NM_IP4_CONFIG_WINS_SERVERS), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServers",
+                                                           "au",
+                                                           NM_IP4_CONFIG_WINS_SERVERS), ), ),
 };
 
 static void
diff --git a/src/core/nm-ip4-config.h b/src/core/nm-ip4-config.h
index a795e152..fdaa6d46 100644
--- a/src/core/nm-ip4-config.h
+++ b/src/core/nm-ip4-config.h
@@ -10,8 +10,8 @@
 
 #include "nm-setting-ip4-config.h"
 
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "platform/nmp-object.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-platform/nmp-object.h"
 #include "nm-ip-config.h"
 
 /*****************************************************************************/
@@ -664,4 +664,7 @@ nm_ip_config_intersect_alloc(const NMIPConfig *a,
     }
 }
 
+gconstpointer nm_ip_config_find_first_address(const NMIPConfig *   self,
+                                              NMPlatformMatchFlags match_flag);
+
 #endif /* __NETWORKMANAGER_IP4_CONFIG_H__ */
diff --git a/src/core/nm-ip6-config.c b/src/core/nm-ip6-config.c
index 1f7def34..d2ecf175 100644
--- a/src/core/nm-ip6-config.c
+++ b/src/core/nm-ip6-config.c
@@ -13,13 +13,13 @@
 #include <linux/rtnetlink.h>
 #include <linux/if.h>
 
-#include "nm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
 
 #include "nm-utils.h"
-#include "platform/nmp-object.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "nm-ip4-config.h"
 #include "ndisc/nm-ndisc.h"
@@ -396,23 +396,6 @@ nm_ip6_config_add_dependent_routes(NMIP6Config *self,
      *
      * For manually added IPv6 routes, add the device routes explicitly. */
 
-    /* Pre-generate multicast route */
-    {
-        nm_auto_nmpobj NMPObject *r = NULL;
-        NMPlatformIP6Route *      route;
-
-        r                         = nmp_object_new(NMP_OBJECT_TYPE_IP6_ROUTE, NULL);
-        route                     = NMP_OBJECT_CAST_IP6_ROUTE(r);
-        route->ifindex            = ifindex;
-        route->network.s6_addr[0] = 0xffu;
-        route->plen               = 8;
-        route->table_coerced      = nm_platform_route_table_coerce(RT_TABLE_LOCAL);
-        route->type_coerced       = nm_platform_route_type_coerce(RTN_UNICAST);
-        route->metric             = 256;
-
-        _add_route(self, r, NULL, NULL);
-    }
-
     nm_ip_config_iter_ip6_address_for_each (&iter, self, &my_addr) {
         NMPlatformIP6Route *route;
         gboolean            has_peer;
@@ -421,22 +404,6 @@ nm_ip6_config_add_dependent_routes(NMIP6Config *self,
         if (my_addr->external)
             continue;
 
-        {
-            nm_auto_nmpobj NMPObject *r = NULL;
-
-            /* Pre-generate local route added by kernel */
-            r                   = nmp_object_new(NMP_OBJECT_TYPE_IP6_ROUTE, NULL);
-            route               = NMP_OBJECT_CAST_IP6_ROUTE(r);
-            route->ifindex      = ifindex;
-            route->network      = my_addr->address;
-            route->plen         = 128;
-            route->type_coerced = nm_platform_route_type_coerce(RTN_LOCAL);
-            route->metric       = 0;
-            route->table_coerced =
-                nm_platform_route_table_coerce(is_vrf ? route_table : RT_TABLE_LOCAL);
-            _add_route(self, r, NULL, NULL);
-        }
-
         if (NM_FLAGS_HAS(my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE))
             continue;
         if (my_addr->plen == 0)
@@ -1661,28 +1628,6 @@ nm_ip6_config_lookup_address(const NMIP6Config *self, const struct in6_addr *add
     return entry ? NMP_OBJECT_CAST_IP6_ADDRESS(entry->obj) : NULL;
 }
 
-const NMPlatformIP6Address *
-nm_ip6_config_find_first_address(const NMIP6Config *self, NMPlatformMatchFlags match_flag)
-{
-    const NMPlatformIP6Address *addr;
-    NMDedupMultiIter            iter;
-
-    g_return_val_if_fail(NM_IS_IP6_CONFIG(self), NULL);
-
-    nm_assert(!NM_FLAGS_ANY(
-        match_flag,
-        ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)));
-
-    nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY));
-    nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY));
-
-    nm_ip_config_iter_ip6_address_for_each (&iter, self, &addr) {
-        if (nm_platform_ip6_address_match(addr, match_flag))
-            return addr;
-    }
-    return NULL;
-}
-
 /**
  * nm_ip6_config_has_dad_pending_addresses
  * @self: configuration containing the addresses to check
@@ -2442,9 +2387,7 @@ nameservers_to_gvalue(GArray *array, GValue *value)
         struct in6_addr *addr;
 
         addr = &g_array_index(array, struct in6_addr, i++);
-        g_variant_builder_add(&builder,
-                              "@ay",
-                              g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, addr, 16, 1));
+        g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(addr));
     }
 
     g_value_take_variant(value, g_variant_builder_end(&builder));
@@ -2620,37 +2563,33 @@ finalize(GObject *object)
 static const NMDBusInterfaceInfoExtended interface_info_ip6_config = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_IP6_CONFIG,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Addresses",
-                                                             "a(ayuay)",
-                                                             NM_IP6_CONFIG_ADDRESSES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("AddressData",
-                                                             "aa{sv}",
-                                                             NM_IP6_CONFIG_ADDRESS_DATA),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Gateway", "s", NM_IP6_CONFIG_GATEWAY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Routes",
-                                                             "a(ayuayu)",
-                                                             NM_IP6_CONFIG_ROUTES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RouteData",
-                                                             "aa{sv}",
-                                                             NM_IP6_CONFIG_ROUTE_DATA),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Nameservers",
-                                                             "aay",
-                                                             NM_IP6_CONFIG_NAMESERVERS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Domains",
-                                                             "as",
-                                                             NM_IP6_CONFIG_DOMAINS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Searches",
-                                                             "as",
-                                                             NM_IP6_CONFIG_SEARCHES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("DnsOptions",
-                                                             "as",
-                                                             NM_IP6_CONFIG_DNS_OPTIONS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("DnsPriority",
-                                                             "i",
-                                                             NM_IP6_CONFIG_DNS_PRIORITY), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Addresses",
+                                                           "a(ayuay)",
+                                                           NM_IP6_CONFIG_ADDRESSES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData",
+                                                           "aa{sv}",
+                                                           NM_IP6_CONFIG_ADDRESS_DATA),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP6_CONFIG_GATEWAY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Routes",
+                                                           "a(ayuayu)",
+                                                           NM_IP6_CONFIG_ROUTES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RouteData",
+                                                           "aa{sv}",
+                                                           NM_IP6_CONFIG_ROUTE_DATA),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Nameservers",
+                                                           "aay",
+                                                           NM_IP6_CONFIG_NAMESERVERS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Domains", "as", NM_IP6_CONFIG_DOMAINS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Searches",
+                                                           "as",
+                                                           NM_IP6_CONFIG_SEARCHES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsOptions",
+                                                           "as",
+                                                           NM_IP6_CONFIG_DNS_OPTIONS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsPriority",
+                                                           "i",
+                                                           NM_IP6_CONFIG_DNS_PRIORITY), ), ),
 };
 
 static void
diff --git a/src/core/nm-ip6-config.h b/src/core/nm-ip6-config.h
index 398186da..8694ab0c 100644
--- a/src/core/nm-ip6-config.h
+++ b/src/core/nm-ip6-config.h
@@ -10,8 +10,8 @@
 
 #include "nm-setting-ip6-config.h"
 
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "platform/nmp-object.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-platform/nmp-object.h"
 #include "nm-ip-config.h"
 
 /*****************************************************************************/
@@ -125,8 +125,6 @@ void  _nmtst_ip6_config_del_address(NMIP6Config *self, guint i);
 guint nm_ip6_config_get_num_addresses(const NMIP6Config *self);
 const NMPlatformIP6Address *nm_ip6_config_get_first_address(const NMIP6Config *self);
 const NMPlatformIP6Address *_nmtst_ip6_config_get_address(const NMIP6Config *self, guint i);
-const NMPlatformIP6Address *nm_ip6_config_find_first_address(const NMIP6Config *  self,
-                                                             NMPlatformMatchFlags match_flag);
 gboolean nm_ip6_config_address_exists(const NMIP6Config *self, const NMPlatformIP6Address *address);
 const NMPlatformIP6Address *nm_ip6_config_lookup_address(const NMIP6Config *    self,
                                                          const struct in6_addr *addr);
diff --git a/src/core/nm-keep-alive.c b/src/core/nm-keep-alive.c
index 197e30ea..6275544c 100644
--- a/src/core/nm-keep-alive.c
+++ b/src/core/nm-keep-alive.c
@@ -8,7 +8,7 @@
 #include "nm-keep-alive.h"
 
 #include "settings/nm-settings-connection.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c
index 5f9af7ac..f2ec040b 100644
--- a/src/core/nm-l3-config-data.c
+++ b/src/core/nm-l3-config-data.c
@@ -8,11 +8,12 @@
 #include <linux/if_addr.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-glib-aux/nm-enum-utils.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "platform/nmp-object.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-glib-aux/nm-enum-utils.h"
+#include "libnm-glib-aux/nm-ref-string.h"
+#include "libnm-platform/nm-platform-utils.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
 #include "NetworkManagerUtils.h"
 
 /*****************************************************************************/
@@ -52,7 +53,9 @@ struct _NML3ConfigData {
     GArray *wins;
     GArray *nis_servers;
 
-    char *nis_domain;
+    NMRefString *nis_domain;
+    NMRefString *proxy_pac_url;
+    NMRefString *proxy_pac_script;
 
     union {
         struct {
@@ -130,6 +133,10 @@ struct _NML3ConfigData {
 
     NMTernary metered : 3;
 
+    NMTernary proxy_browser_only : 3;
+
+    NMProxyConfigMethod proxy_method : 4;
+
     NMSettingIP6ConfigPrivacy ip6_privacy : 4;
 
     bool is_sealed : 1;
@@ -251,7 +258,7 @@ _garray_inaddr_get(GArray *arr, guint *out_len)
 }
 
 static gconstpointer
-_garray_inaddr_at(GArray *arr, guint idx, gboolean IS_IPv4)
+_garray_inaddr_at(GArray *arr, gboolean IS_IPv4, guint idx)
 {
     nm_assert(arr);
     nm_assert(idx < arr->len);
@@ -507,7 +514,7 @@ nm_l3_config_data_log(const NML3ConfigData *self,
                    _nm_utils_inet4_ntop(g_array_index(self->nis_servers, in_addr_t, i), sbuf_addr));
             }
             if (self->nis_domain)
-                _L("nis-domain: %s", self->nis_domain);
+                _L("nis-domain: %s", self->nis_domain->str);
         }
 
         if (self->dhcp_lease_x[IS_IPv4]) {
@@ -556,10 +563,20 @@ nm_l3_config_data_log(const NML3ConfigData *self,
                                            NULL)));
     }
 
-    if (self->metered != NM_TERNARY_DEFAULT) {
+    if (self->metered != NM_TERNARY_DEFAULT)
         _L("metered: %s", self->metered ? "yes" : "no");
-    }
 
+    if (self->proxy_browser_only != NM_TERNARY_DEFAULT)
+        _L("proxy-browser-only: %s", self->proxy_browser_only ? "yes" : "no");
+
+    if (self->proxy_method != NM_PROXY_CONFIG_METHOD_UNKNOWN)
+        _L("proxy-method: %s", self->proxy_method == NM_PROXY_CONFIG_METHOD_AUTO ? "auto" : "none");
+
+    if (self->proxy_pac_url)
+        _L("proxy-pac-url: %s", self->proxy_pac_url->str);
+
+    if (self->proxy_pac_script)
+        _L("proxy-pac-script: %s", self->proxy_pac_script->str);
 #undef _L
 }
 
@@ -642,6 +659,8 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex)
         .llmnr                         = NM_SETTING_CONNECTION_LLMNR_DEFAULT,
         .flags                         = NM_L3_CONFIG_DAT_FLAGS_NONE,
         .metered                       = NM_TERNARY_DEFAULT,
+        .proxy_browser_only            = NM_TERNARY_DEFAULT,
+        .proxy_method                  = NM_PROXY_CONFIG_METHOD_UNKNOWN,
         .route_table_sync_4            = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
         .route_table_sync_6            = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
         .source                        = NM_IP_CONFIG_SOURCE_UNKNOWN,
@@ -742,7 +761,9 @@ nm_l3_config_data_unref(const NML3ConfigData *self)
 
     nm_dedup_multi_index_unref(mutable->multi_idx);
 
-    g_free(mutable->nis_domain);
+    nm_ref_string_unref(mutable->nis_domain);
+    nm_ref_string_unref(mutable->proxy_pac_url);
+    nm_ref_string_unref(mutable->proxy_pac_script);
 
     nm_g_slice_free(mutable);
 }
@@ -796,8 +817,8 @@ nm_l3_config_data_lookup_route(const NML3ConfigData *   self,
                                int                      addr_family,
                                const NMPlatformIPRoute *needle)
 {
-    const gboolean IS_IPv4 = NM_IS_IPv4(addr_family);
-    NMPObject      obj_stack;
+    const int IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMPObject obj_stack;
 
     nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
     nm_assert_addr_family(addr_family);
@@ -895,7 +916,7 @@ nmtst_l3_config_data_get_obj_at(const NML3ConfigData *self, NMPObjectType obj_ty
 gboolean
 nm_l3_config_data_has_routes_with_type_local(const NML3ConfigData *self, int addr_family)
 {
-    const gboolean   IS_IPv4 = NM_IS_IPv4(addr_family);
+    const int        IS_IPv4 = NM_IS_IPv4(addr_family);
     NML3ConfigData * self_mutable;
     NMDedupMultiIter iter;
     const NMPObject *obj;
@@ -1178,7 +1199,7 @@ _l3_config_best_default_route_find_better(const NMPObject *obj_cur, const NMPObj
                 return c < 0 ? obj_cur : obj_cmp;
 
             /* as last resort, compare pointers. */
-            if (((uintptr_t)((void *) (obj_cmp))) < ((uintptr_t)((void *) (obj_cur))))
+            if (((uintptr_t) ((void *) (obj_cmp))) < ((uintptr_t) ((void *) (obj_cur))))
                 return obj_cmp;
         }
     }
@@ -1230,7 +1251,7 @@ nm_l3_config_data_add_route_full(NML3ConfigData *         self,
                                  const NMPObject **       out_obj_new,
                                  gboolean *               out_changed_best_default_route)
 {
-    const gboolean       IS_IPv4            = NM_IS_IPv4(addr_family);
+    const int            IS_IPv4            = NM_IS_IPv4(addr_family);
     nm_auto_nmpobj const NMPObject *obj_old = NULL;
     const NMPObject *               obj_new_2;
     gboolean                        changed                    = FALSE;
@@ -1364,6 +1385,14 @@ nm_l3_config_data_add_wins(NML3ConfigData *self, in_addr_t wins)
     return _garray_inaddr_add(&self->wins, AF_INET, &wins);
 }
 
+const in_addr_t *
+nm_l3_config_data_get_nis_servers(const NML3ConfigData *self, guint *out_len)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return _garray_inaddr_get(self->nis_servers, out_len);
+}
+
 gboolean
 nm_l3_config_data_add_nis_server(NML3ConfigData *self, in_addr_t nis_server)
 {
@@ -1372,18 +1401,26 @@ nm_l3_config_data_add_nis_server(NML3ConfigData *self, in_addr_t nis_server)
     return _garray_inaddr_add(&self->nis_servers, AF_INET, &nis_server);
 }
 
+const char *
+nm_l3_config_data_get_nis_domain(const NML3ConfigData *self)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return nm_ref_string_get_str(self->nis_domain);
+}
+
 gboolean
 nm_l3_config_data_set_nis_domain(NML3ConfigData *self, const char *nis_domain)
 {
     nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
 
-    return nm_utils_strdup_reset(&self->nis_domain, nis_domain);
+    return nm_ref_string_reset_str(&self->nis_domain, nis_domain);
 }
 
 const char *const *
 nm_l3_config_data_get_domains(const NML3ConfigData *self, int addr_family, guint *out_len)
 {
-    nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
     nm_assert_addr_family(addr_family);
     nm_assert(out_len);
 
@@ -1452,10 +1489,57 @@ nm_l3_config_data_add_dns_option(NML3ConfigData *self, int addr_family, const ch
     return TRUE;
 }
 
+const char *const *
+nm_l3_config_data_get_dns_options(const NML3ConfigData *self, int addr_family, guint *out_len)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+    nm_assert_addr_family(addr_family);
+    nm_assert(out_len);
+
+    return nm_strv_ptrarray_get_unsafe(self->dns_options_x[NM_IS_IPv4(addr_family)], out_len);
+}
+
+gboolean
+nm_l3_config_data_get_dns_priority(const NML3ConfigData *self, int addr_family, int *out_prio)
+{
+    switch (addr_family) {
+    case AF_UNSPEC:
+        if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_4)) {
+            if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6)) {
+                NM_SET_OUT(out_prio, MIN(self->dns_priority_4, self->dns_priority_6));
+                return TRUE;
+            }
+            NM_SET_OUT(out_prio, self->dns_priority_4);
+            return TRUE;
+        }
+        if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6)) {
+            NM_SET_OUT(out_prio, self->dns_priority_6);
+            return TRUE;
+        }
+        break;
+    case AF_INET:
+        if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_4)) {
+            NM_SET_OUT(out_prio, self->dns_priority_4);
+            return TRUE;
+        }
+        break;
+    case AF_INET6:
+        if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6)) {
+            NM_SET_OUT(out_prio, self->dns_priority_6);
+            return TRUE;
+        }
+        break;
+    default:
+        nm_assert_not_reached();
+    }
+    NM_SET_OUT(out_prio, 0);
+    return FALSE;
+}
+
 gboolean
 nm_l3_config_data_set_dns_priority(NML3ConfigData *self, int addr_family, int dns_priority)
 {
-    const gboolean           IS_IPv4 = NM_IS_IPv4(addr_family);
+    const int                IS_IPv4 = NM_IS_IPv4(addr_family);
     const NML3ConfigDatFlags has_dns_priority_flag =
         NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY(IS_IPv4);
 
@@ -1471,6 +1555,14 @@ nm_l3_config_data_set_dns_priority(NML3ConfigData *self, int addr_family, int dn
     return TRUE;
 }
 
+NMSettingConnectionMdns
+nm_l3_config_data_get_mdns(const NML3ConfigData *self)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return self->mdns;
+}
+
 gboolean
 nm_l3_config_data_set_mdns(NML3ConfigData *self, NMSettingConnectionMdns mdns)
 {
@@ -1483,6 +1575,14 @@ nm_l3_config_data_set_mdns(NML3ConfigData *self, NMSettingConnectionMdns mdns)
     return TRUE;
 }
 
+NMSettingConnectionLlmnr
+nm_l3_config_data_get_llmnr(const NML3ConfigData *self)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return self->llmnr;
+}
+
 gboolean
 nm_l3_config_data_set_llmnr(NML3ConfigData *self, NMSettingConnectionLlmnr llmnr)
 {
@@ -1509,7 +1609,7 @@ nm_l3_config_data_set_route_table_sync(NML3ConfigData *       self,
                                        int                    addr_family,
                                        NMIPRouteTableSyncMode route_table_sync)
 {
-    const gboolean IS_IPv4 = NM_IS_IPv4(addr_family);
+    const int IS_IPv4 = NM_IS_IPv4(addr_family);
 
     nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
     nm_assert_addr_family(addr_family);
@@ -1533,7 +1633,7 @@ gboolean
 nm_l3_config_data_set_metered(NML3ConfigData *self, NMTernary metered)
 {
     nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
-    nm_assert(NM_IN_SET(metered, NM_TERNARY_DEFAULT, NM_TERNARY_FALSE, NM_TERNARY_TRUE));
+    nm_assert_is_ternary(metered);
 
     if (self->metered == metered)
         return FALSE;
@@ -1619,6 +1719,83 @@ nm_l3_config_data_set_ip6_privacy(NML3ConfigData *self, NMSettingIP6ConfigPrivac
     return TRUE;
 }
 
+NMProxyConfigMethod
+nm_l3_config_data_get_proxy_method(const NML3ConfigData *self)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return self->proxy_method;
+}
+
+gboolean
+nm_l3_config_data_set_proxy_method(NML3ConfigData *self, NMProxyConfigMethod value)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
+    nm_assert(NM_IN_SET(value,
+                        NM_PROXY_CONFIG_METHOD_UNKNOWN,
+                        NM_PROXY_CONFIG_METHOD_NONE,
+                        NM_PROXY_CONFIG_METHOD_AUTO));
+
+    if (self->proxy_method == value)
+        return FALSE;
+
+    self->proxy_method = value;
+    return TRUE;
+}
+
+NMTernary
+nm_l3_config_data_get_proxy_browser_only(const NML3ConfigData *self)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return self->proxy_browser_only;
+}
+
+gboolean
+nm_l3_config_data_set_proxy_browser_only(NML3ConfigData *self, NMTernary value)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
+    nm_assert_is_ternary(value);
+
+    if (value == self->proxy_browser_only)
+        return FALSE;
+
+    self->proxy_browser_only = value;
+    return TRUE;
+}
+
+const char *
+nm_l3_config_data_get_proxy_pac_url(const NML3ConfigData *self)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return nm_ref_string_get_str(self->proxy_pac_url);
+}
+
+gboolean
+nm_l3_config_data_set_proxy_pac_url(NML3ConfigData *self, const char *value)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
+
+    return nm_ref_string_reset_str(&self->proxy_pac_url, value);
+}
+
+const char *
+nm_l3_config_data_get_proxy_pac_script(const NML3ConfigData *self)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
+
+    return nm_ref_string_get_str(self->proxy_pac_script);
+}
+
+gboolean
+nm_l3_config_data_set_proxy_pac_script(NML3ConfigData *self, const char *value)
+{
+    nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
+
+    return nm_ref_string_reset_str(&self->proxy_pac_script, value);
+}
+
 gboolean
 nm_l3_config_data_get_ndisc_hop_limit(const NML3ConfigData *self, int *out_val)
 {
@@ -1780,13 +1957,16 @@ _dedup_multi_index_cmp(const NML3ConfigData *a, const NML3ConfigData *b, NMPObje
 }
 
 int
-nm_l3_config_data_cmp(const NML3ConfigData *a, const NML3ConfigData *b)
+nm_l3_config_data_cmp_full(const NML3ConfigData *a,
+                           const NML3ConfigData *b,
+                           NML3ConfigCmpFlags    cmp_flags)
 {
     int IS_IPv4;
 
     NM_CMP_SELF(a, b);
 
-    NM_CMP_DIRECT(a->ifindex, b->ifindex);
+    if (!NM_FLAGS_HAS(cmp_flags, NM_L3_CONFIG_CMP_FLAGS_IGNORE_IFINDEX))
+        NM_CMP_DIRECT(a->ifindex, b->ifindex);
 
     NM_CMP_DIRECT(a->flags, b->flags);
 
@@ -1823,12 +2003,16 @@ nm_l3_config_data_cmp(const NML3ConfigData *a, const NML3ConfigData *b)
 
     NM_CMP_RETURN(_garray_inaddr_cmp(a->wins, b->wins, AF_INET));
     NM_CMP_RETURN(_garray_inaddr_cmp(a->nis_servers, b->nis_servers, AF_INET));
-    NM_CMP_FIELD_STR0(a, b, nis_domain);
+    NM_CMP_DIRECT_REF_STRING(a->nis_domain, b->nis_domain);
     NM_CMP_DIRECT(a->mdns, b->mdns);
     NM_CMP_DIRECT(a->llmnr, b->llmnr);
     NM_CMP_DIRECT(a->mtu, b->mtu);
     NM_CMP_DIRECT(a->ip6_mtu, b->ip6_mtu);
     NM_CMP_DIRECT_UNSAFE(a->metered, b->metered);
+    NM_CMP_DIRECT_UNSAFE(a->proxy_browser_only, b->proxy_browser_only);
+    NM_CMP_DIRECT_UNSAFE(a->proxy_method, b->proxy_method);
+    NM_CMP_DIRECT_REF_STRING(a->proxy_pac_url, b->proxy_pac_url);
+    NM_CMP_DIRECT_REF_STRING(a->proxy_pac_script, b->proxy_pac_script);
     NM_CMP_DIRECT_UNSAFE(a->ip6_privacy, b->ip6_privacy);
 
     NM_CMP_DIRECT_UNSAFE(a->ndisc_hop_limit_set, b->ndisc_hop_limit_set);
@@ -1863,7 +2047,7 @@ _data_get_direct_route_for_host(const NML3ConfigData *self,
                                 gconstpointer         host,
                                 guint32               route_table)
 {
-    const gboolean            IS_IPv4        = NM_IS_IPv4(addr_family);
+    const int                 IS_IPv4        = NM_IS_IPv4(addr_family);
     const NMPObject *         best_route_obj = NULL;
     const NMPlatformIPXRoute *best_route     = NULL;
     const NMPObject *         item_obj;
@@ -1998,7 +2182,7 @@ nm_l3_config_data_add_dependent_routes(NML3ConfigData *self,
                                        guint32         route_metric,
                                        gboolean        is_vrf)
 {
-    const gboolean    IS_IPv4                        = NM_IS_IPv4(addr_family);
+    const int         IS_IPv4                        = NM_IS_IPv4(addr_family);
     gs_unref_ptrarray GPtrArray *extra_onlink_routes = NULL;
     const NMPObject *            my_addr_obj;
     const NMPObject *            my_route_obj;
@@ -2209,7 +2393,7 @@ _init_from_connection_ip(NML3ConfigData *self,
                          guint32         route_table,
                          guint32         route_metric)
 {
-    const gboolean     IS_IPv4 = NM_IS_IPv4(addr_family);
+    const int          IS_IPv4 = NM_IS_IPv4(addr_family);
     NMSettingIPConfig *s_ip;
     guint              naddresses;
     guint              nroutes;
@@ -2388,11 +2572,28 @@ nm_l3_config_data_new_from_connection(NMDedupMultiIndex *multi_idx,
                                       guint32            route_metric_6)
 {
     NML3ConfigData *self;
+    NMSettingProxy *s_proxy;
 
     self = nm_l3_config_data_new(multi_idx, ifindex);
 
     _init_from_connection_ip(self, AF_INET, connection, route_table_4, route_metric_4);
     _init_from_connection_ip(self, AF_INET6, connection, route_table_6, route_metric_6);
+
+    s_proxy = _nm_connection_get_setting(connection, NM_TYPE_SETTING_PROXY);
+    if (s_proxy) {
+        switch (nm_setting_proxy_get_method(s_proxy)) {
+        case NM_SETTING_PROXY_METHOD_NONE:
+            self->proxy_method = NM_PROXY_CONFIG_METHOD_NONE;
+            break;
+        case NM_SETTING_PROXY_METHOD_AUTO:
+            self->proxy_method     = NM_PROXY_CONFIG_METHOD_AUTO;
+            self->proxy_pac_url    = nm_ref_string_new(nm_setting_proxy_get_pac_url(s_proxy));
+            self->proxy_pac_script = nm_ref_string_new(nm_setting_proxy_get_pac_script(s_proxy));
+            break;
+        }
+        self->proxy_browser_only = (!!nm_setting_proxy_get_browser_only(s_proxy));
+    }
+
     return self;
 }
 
@@ -2433,7 +2634,7 @@ _init_from_platform(NML3ConfigData *          self,
                     NMPlatform *              platform,
                     NMSettingIP6ConfigPrivacy ipv6_privacy_rfc4941)
 {
-    const gboolean               IS_IPv4 = NM_IS_IPv4(addr_family);
+    const int                    IS_IPv4 = NM_IS_IPv4(addr_family);
     const NMDedupMultiHeadEntry *head_entry;
     const NMPObject *            plobj = NULL;
     NMDedupMultiIter             iter;
@@ -2534,6 +2735,9 @@ nm_l3_config_data_merge(NML3ConfigData *      self,
     nm_assert(default_route_table_x[1] != 0);
     nm_assert(default_route_metric_x[0] != 0); /* IPv6 route metric cannot be zero. */
 
+    nm_assert(!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)
+              || nm_utils_is_power_of_two(merge_flags));
+
     for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
         const int                addr_family = IS_IPv4 ? AF_INET : AF_INET6;
         const NML3ConfigDatFlags has_dns_priority_flag =
@@ -2588,28 +2792,31 @@ nm_l3_config_data_merge(NML3ConfigData *      self,
     }                                                   \
     G_STMT_END
 
-                if (r_src->table_any) {
-                    _ensure_r();
-                    r.rx.table_any     = FALSE;
-                    r.rx.table_coerced = default_route_table_x[IS_IPv4];
-                }
-
-                if (r_src->metric_any) {
-                    _ensure_r();
-                    r.rx.metric_any = FALSE;
-                    r.rx.metric = nm_add_clamped_u32(r.rx.metric, default_route_metric_x[IS_IPv4]);
-                }
+                if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) {
+                    if (r_src->table_any) {
+                        _ensure_r();
+                        r.rx.table_any     = FALSE;
+                        r.rx.table_coerced = default_route_table_x[IS_IPv4];
+                    }
 
-                if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r_src)) {
-                    if (NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES)
-                        && !NM_FLAGS_HAS(src->flags,
-                                         NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES))
-                        continue;
-                    if (default_route_penalty_x && default_route_penalty_x[IS_IPv4] > 0) {
+                    if (r_src->metric_any) {
                         _ensure_r();
+                        r.rx.metric_any = FALSE;
                         r.rx.metric =
-                            nm_utils_ip_route_metric_penalize(r.rx.metric,
-                                                              default_route_penalty_x[IS_IPv4]);
+                            nm_add_clamped_u32(r.rx.metric, default_route_metric_x[IS_IPv4]);
+                    }
+
+                    if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r_src)) {
+                        if (NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES)
+                            && !NM_FLAGS_HAS(src->flags,
+                                             NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES))
+                            continue;
+                        if (default_route_penalty_x && default_route_penalty_x[IS_IPv4] > 0) {
+                            _ensure_r();
+                            r.rx.metric =
+                                nm_utils_ip_route_metric_penalize(r.rx.metric,
+                                                                  default_route_penalty_x[IS_IPv4]);
+                        }
                     }
                 }
 
@@ -2653,8 +2860,8 @@ nm_l3_config_data_merge(NML3ConfigData *      self,
         _garray_inaddr_merge(&self->wins, src->wins, AF_INET);
         _garray_inaddr_merge(&self->nis_servers, src->nis_servers, AF_INET);
 
-        if (!self->nis_domain && src->nis_domain)
-            self->nis_domain = g_strdup(src->nis_domain);
+        if (!self->nis_domain)
+            self->nis_domain = nm_ref_string_ref(src->nis_domain);
     }
 
     if (self->mdns == NM_SETTING_CONNECTION_MDNS_DEFAULT)
@@ -2665,6 +2872,18 @@ nm_l3_config_data_merge(NML3ConfigData *      self,
 
     self->metered = NM_MAX((NMTernary) self->metered, (NMTernary) src->metered);
 
+    if (self->proxy_method == NM_PROXY_CONFIG_METHOD_UNKNOWN)
+        self->proxy_method = src->proxy_method;
+
+    if (self->proxy_browser_only == NM_TERNARY_DEFAULT)
+        self->proxy_browser_only = src->proxy_browser_only;
+
+    if (!self->proxy_pac_url)
+        self->proxy_pac_url = nm_ref_string_ref(src->proxy_pac_url);
+
+    if (!self->proxy_pac_script)
+        self->proxy_pac_script = nm_ref_string_ref(src->proxy_pac_script);
+
     if (self->ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
         self->ip6_privacy = src->ip6_privacy;
 
@@ -2689,8 +2908,16 @@ nm_l3_config_data_merge(NML3ConfigData *      self,
     if (self->ip6_mtu == 0u)
         self->ip6_mtu = src->ip6_mtu;
 
-    /* self->source does not get merged. */
-    /* self->dhcp_lease_x does not get merged. */
+    if (NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) {
+        _nm_unused nm_auto_unref_dhcplease NMDhcpLease *dhcp_lease_6 =
+            g_steal_pointer(&self->dhcp_lease_x[0]);
+        _nm_unused nm_auto_unref_dhcplease NMDhcpLease *dhcp_lease_4 =
+            g_steal_pointer(&self->dhcp_lease_x[1]);
+
+        self->source          = src->source;
+        self->dhcp_lease_x[0] = nm_dhcp_lease_ref(self->dhcp_lease_x[0]);
+        self->dhcp_lease_x[1] = nm_dhcp_lease_ref(self->dhcp_lease_x[1]);
+    }
 }
 
 NML3ConfigData *
@@ -2707,6 +2934,17 @@ nm_l3_config_data_new_clone(const NML3ConfigData *src, int ifindex)
         ifindex = src->ifindex;
 
     self = nm_l3_config_data_new(src->multi_idx, ifindex);
-    nm_l3_config_data_merge(self, src, NM_L3_CONFIG_MERGE_FLAGS_NONE, NULL, NULL, NULL, NULL, NULL);
+    nm_l3_config_data_merge(self,
+                            src,
+                            NM_L3_CONFIG_MERGE_FLAGS_CLONE,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL);
+
+    nm_assert(nm_l3_config_data_cmp_full(src, self, NM_L3_CONFIG_CMP_FLAGS_IGNORE_IFINDEX) == 0);
+    nm_assert(nm_l3_config_data_get_ifindex(self) == ifindex);
+
     return self;
 }
diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h
index b3d9cd32..9bed47e0 100644
--- a/src/core/nm-l3-config-data.h
+++ b/src/core/nm-l3-config-data.h
@@ -3,11 +3,17 @@
 #ifndef __NM_L3_CONFIG_DATA_H__
 #define __NM_L3_CONFIG_DATA_H__
 
-#include "nm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-ip6-config.h"
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
+
+typedef enum {
+    NM_PROXY_CONFIG_METHOD_UNKNOWN,
+    NM_PROXY_CONFIG_METHOD_NONE,
+    NM_PROXY_CONFIG_METHOD_AUTO,
+} NMProxyConfigMethod;
 
 typedef enum {
     NM_L3_CONFIG_DAT_FLAGS_NONE = 0,
@@ -60,6 +66,8 @@ typedef enum {
  *   Note that if the respective NML3ConfigData has NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES
  *   set, this flag gets ignored during merge.
  * @NM_L3_CONFIG_MERGE_FLAGS_NO_DNS: don't merge DNS information
+ * @NM_L3_CONFIG_MERGE_FLAGS_CLONE: clone is also implemented via "merge".
+ *   In that case, it takes all settings.
  */
 typedef enum _nm_packed {
     NM_L3_CONFIG_MERGE_FLAGS_NONE              = 0,
@@ -67,6 +75,7 @@ typedef enum _nm_packed {
     NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES         = (1LL << 1),
     NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES = (1LL << 2),
     NM_L3_CONFIG_MERGE_FLAGS_NO_DNS            = (1LL << 3),
+    NM_L3_CONFIG_MERGE_FLAGS_CLONE             = (1LL << 4),
 } NML3ConfigMergeFlags;
 
 /*****************************************************************************/
@@ -191,7 +200,20 @@ NMDedupMultiIndex *nm_l3_config_data_get_multi_idx(const NML3ConfigData *self);
 
 /*****************************************************************************/
 
-int nm_l3_config_data_cmp(const NML3ConfigData *a, const NML3ConfigData *b);
+typedef enum {
+    NM_L3_CONFIG_CMP_FLAGS_NONE,
+    NM_L3_CONFIG_CMP_FLAGS_IGNORE_IFINDEX = (1LL << 0),
+} NML3ConfigCmpFlags;
+
+int nm_l3_config_data_cmp_full(const NML3ConfigData *a,
+                               const NML3ConfigData *b,
+                               NML3ConfigCmpFlags    cmp_flags);
+
+static inline int
+nm_l3_config_data_cmp(const NML3ConfigData *a, const NML3ConfigData *b)
+{
+    return nm_l3_config_data_cmp_full(a, b, NM_L3_CONFIG_CMP_FLAGS_NONE);
+}
 
 static inline gboolean
 nm_l3_config_data_equal(const NML3ConfigData *a, const NML3ConfigData *b)
@@ -236,6 +258,11 @@ nm_l3_config_data_lookup_routes(const NML3ConfigData *self, int addr_family)
     for (nm_dedup_multi_iter_init((iter), nm_l3_config_data_lookup_objs((self), (type))); \
          nm_platform_dedup_multi_iter_next_obj((iter), (obj), (type));)
 
+#define nm_l3_config_data_iter_ip_address_for_each(iter, self, addr_family, address)          \
+    for (nm_dedup_multi_iter_init((iter),                                                     \
+                                  nm_l3_config_data_lookup_addresses((self), (addr_family))); \
+         nm_platform_dedup_multi_iter_next_ip_address((iter), (address));)
+
 #define nm_l3_config_data_iter_ip4_address_for_each(iter, self, address)                        \
     for (nm_dedup_multi_iter_init((iter), nm_l3_config_data_lookup_addresses((self), AF_INET)); \
          nm_platform_dedup_multi_iter_next_ip4_address((iter), (address));)
@@ -244,6 +271,10 @@ nm_l3_config_data_lookup_routes(const NML3ConfigData *self, int addr_family)
     for (nm_dedup_multi_iter_init((iter), nm_l3_config_data_lookup_addresses((self), AF_INET6)); \
          nm_platform_dedup_multi_iter_next_ip6_address((iter), (address));)
 
+#define nm_l3_config_data_iter_ip_route_for_each(iter, self, addr_family, route)                   \
+    for (nm_dedup_multi_iter_init((iter), nm_l3_config_data_lookup_routes((self), (addr_family))); \
+         nm_platform_dedup_multi_iter_next_ip_route((iter), (route));)
+
 #define nm_l3_config_data_iter_ip4_route_for_each(iter, self, route)                         \
     for (nm_dedup_multi_iter_init((iter), nm_l3_config_data_lookup_routes((self), AF_INET)); \
          nm_platform_dedup_multi_iter_next_ip4_route((iter), (route));)
@@ -409,8 +440,12 @@ nm_l3_config_data_add_route_6(NML3ConfigData *self, const NMPlatformIP6Route *rt
 const NMPObject *nm_l3_config_data_get_best_default_route(const NML3ConfigData *self,
                                                           int                   addr_family);
 
+NMSettingConnectionMdns nm_l3_config_data_get_mdns(const NML3ConfigData *self);
+
 gboolean nm_l3_config_data_set_mdns(NML3ConfigData *self, NMSettingConnectionMdns mdns);
 
+NMSettingConnectionLlmnr nm_l3_config_data_get_llmnr(const NML3ConfigData *self);
+
 gboolean nm_l3_config_data_set_llmnr(NML3ConfigData *self, NMSettingConnectionLlmnr llmnr);
 
 NMIPRouteTableSyncMode nm_l3_config_data_get_route_table_sync(const NML3ConfigData *self,
@@ -445,13 +480,17 @@ gboolean nm_l3_config_data_add_nameserver(NML3ConfigData *
 
 gboolean nm_l3_config_data_clear_nameservers(NML3ConfigData *self, int addr_family);
 
+const in_addr_t *nm_l3_config_data_get_nis_servers(const NML3ConfigData *self, guint *out_len);
+
 gboolean nm_l3_config_data_add_nis_server(NML3ConfigData *self, in_addr_t nis_server);
 
-const char *const *
-nm_l3_config_data_get_domains(const NML3ConfigData *self, int addr_family, guint *out_len);
+const char *nm_l3_config_data_get_nis_domain(const NML3ConfigData *self);
 
 gboolean nm_l3_config_data_set_nis_domain(NML3ConfigData *self, const char *nis_domain);
 
+const char *const *
+nm_l3_config_data_get_domains(const NML3ConfigData *self, int addr_family, guint *out_len);
+
 gboolean nm_l3_config_data_add_domain(NML3ConfigData *self, int addr_family, const char *domain);
 
 const char *const *
@@ -464,6 +503,12 @@ gboolean nm_l3_config_data_add_search(NML3ConfigData *self, int addr_family, con
 gboolean
 nm_l3_config_data_add_dns_option(NML3ConfigData *self, int addr_family, const char *dns_option);
 
+const char *const *
+nm_l3_config_data_get_dns_options(const NML3ConfigData *self, int addr_family, guint *out_len);
+
+gboolean
+nm_l3_config_data_get_dns_priority(const NML3ConfigData *self, int addr_family, int *out_prio);
+
 gboolean
 nm_l3_config_data_set_dns_priority(NML3ConfigData *self, int addr_family, int dns_priority);
 
@@ -472,6 +517,22 @@ NMSettingIP6ConfigPrivacy nm_l3_config_data_get_ip6_privacy(const NML3ConfigData
 gboolean nm_l3_config_data_set_ip6_privacy(NML3ConfigData *          self,
                                            NMSettingIP6ConfigPrivacy ip6_privacy);
 
+NMProxyConfigMethod nm_l3_config_data_get_proxy_method(const NML3ConfigData *self);
+
+gboolean nm_l3_config_data_set_proxy_method(NML3ConfigData *self, NMProxyConfigMethod value);
+
+NMTernary nm_l3_config_data_get_proxy_browser_only(const NML3ConfigData *self);
+
+gboolean nm_l3_config_data_set_proxy_browser_only(NML3ConfigData *self, NMTernary value);
+
+const char *nm_l3_config_data_get_proxy_pac_url(const NML3ConfigData *self);
+
+gboolean nm_l3_config_data_set_proxy_pac_url(NML3ConfigData *self, const char *value);
+
+const char *nm_l3_config_data_get_proxy_pac_script(const NML3ConfigData *self);
+
+gboolean nm_l3_config_data_set_proxy_pac_script(NML3ConfigData *self, const char *value);
+
 gboolean nm_l3_config_data_get_ndisc_hop_limit(const NML3ConfigData *self, int *out_val);
 gboolean nm_l3_config_data_set_ndisc_hop_limit(NML3ConfigData *self, int val);
 
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index 839213ad..93e92673 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -9,8 +9,9 @@
 #include <linux/if_ether.h>
 #include <linux/rtnetlink.h>
 
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
+#include "libnm-glib-aux/nm-time-utils.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
 #include "nm-netns.h"
 #include "n-acd/src/n-acd.h"
 #include "nm-l3-ipv4ll.h"
@@ -21,7 +22,7 @@ G_STATIC_ASSERT(NM_ACD_TIMEOUT_RFC5227_MSEC == N_ACD_TIMEOUT_RFC5227);
 
 #define ACD_SUPPORTED_ETH_ALEN                  ETH_ALEN
 #define ACD_ENSURE_RATELIMIT_MSEC               ((guint32) 4000u)
-#define ACD_WAIT_PROBING_EXTRA_TIME_MSEC        ((guint32)(1000u + ACD_ENSURE_RATELIMIT_MSEC))
+#define ACD_WAIT_PROBING_EXTRA_TIME_MSEC        ((guint32) (1000u + ACD_ENSURE_RATELIMIT_MSEC))
 #define ACD_WAIT_PROBING_EXTRA_TIME2_MSEC       ((guint32) 1000u)
 #define ACD_MAX_TIMEOUT_MSEC                    ((guint32) 30000u)
 #define ACD_WAIT_TIME_PROBING_FULL_RESTART_MSEC ((guint32) 30000u)
@@ -301,6 +302,7 @@ static NM_UTILS_ENUM2STR_DEFINE(
     NML3ConfigNotifyType,
     NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT, "acd-event"),
     NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT, "ipv4ll-event"),
+    NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED, "l3cd-changed"),
     NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE, "platform-change"),
     NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE, "platform-change-on-idle"),
     NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT, "post-commit"),
@@ -347,6 +349,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data,
 {
     char      sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
     char      sbuf100[100];
+    char      sbufobf[NM_HASH_OBFUSCATE_PTR_STR_BUF_SIZE];
     char *    s = sbuf;
     gsize     l = sbuf_size;
     in_addr_t addr4;
@@ -358,6 +361,19 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data,
     nm_utils_strbuf_seek_end(&s, &l);
 
     switch (notify_data->notify_type) {
+    case NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED:
+        nm_utils_strbuf_append(
+            &s,
+            &l,
+            ", l3cd-old=%s",
+            NM_HASH_OBFUSCATE_PTR_STR(notify_data->l3cd_changed.l3cd_old, sbufobf));
+        nm_utils_strbuf_append(
+            &s,
+            &l,
+            ", l3cd-new=%s",
+            NM_HASH_OBFUSCATE_PTR_STR(notify_data->l3cd_changed.l3cd_new, sbufobf));
+        nm_utils_strbuf_append(&s, &l, ", commited=%d", notify_data->l3cd_changed.commited);
+        break;
     case NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT:
         nm_utils_strbuf_append(&s,
                                &l,
@@ -423,6 +439,23 @@ _nm_l3cfg_emit_signal_notify_simple(NML3Cfg *self, NML3ConfigNotifyType notify_t
     _nm_l3cfg_emit_signal_notify(self, &notify_data);
 }
 
+static void
+_nm_l3cfg_emit_signal_notify_l3cd_changed(NML3Cfg *             self,
+                                          const NML3ConfigData *l3cd_old,
+                                          const NML3ConfigData *l3cd_new,
+                                          gboolean              commited)
+{
+    NML3ConfigNotifyData notify_data;
+
+    notify_data.notify_type  = NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED;
+    notify_data.l3cd_changed = (typeof(notify_data.l3cd_changed)){
+        .l3cd_old = l3cd_old,
+        .l3cd_new = l3cd_new,
+        .commited = commited,
+    };
+    _nm_l3cfg_emit_signal_notify(self, &notify_data);
+}
+
 /*****************************************************************************/
 
 static void
@@ -1478,7 +1511,7 @@ _l3_acd_data_add(NML3Cfg *             self,
     if (!acd_data) {
         if (G_UNLIKELY(!self->priv.p->acd_lst_hash)) {
             G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(AcdData, info.addr) == 0);
-            self->priv.p->acd_lst_hash = g_hash_table_new(nm_puint32_hash, nm_puint32_equals);
+            self->priv.p->acd_lst_hash = g_hash_table_new(nm_puint32_hash, nm_puint32_equal);
         }
 
         acd_data  = g_slice_new(AcdData);
@@ -2549,18 +2582,19 @@ _l3_commit_on_idle_cb(gpointer user_data)
     return G_SOURCE_REMOVE;
 }
 
-void
+gboolean
 nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self)
 {
     nm_assert(NM_IS_L3CFG(self));
 
     if (self->priv.p->commit_on_idle_source)
-        return;
+        return FALSE;
 
     _LOGT("commit on idle (scheduled)");
     self->priv.p->commit_on_idle_source =
         nm_g_idle_source_new(G_PRIORITY_DEFAULT, _l3_commit_on_idle_cb, self, NULL);
     g_source_attach(self->priv.p->commit_on_idle_source, NULL);
+    return TRUE;
 }
 
 /*****************************************************************************/
@@ -3008,6 +3042,12 @@ _l3cfg_update_combined_config(NML3Cfg *              self,
     l3cd_old                           = g_steal_pointer(&self->priv.p->combined_l3cd_merged);
     self->priv.p->combined_l3cd_merged = nm_l3_config_data_seal(g_steal_pointer(&l3cd));
     merged_changed                     = TRUE;
+
+    _nm_l3cfg_emit_signal_notify_l3cd_changed(self,
+                                              l3cd_old,
+                                              self->priv.p->combined_l3cd_merged,
+                                              FALSE);
+
     if (!to_commit) {
         NM_SET_OUT(out_old, g_steal_pointer(&l3cd_old));
         NM_SET_OUT(out_changed_combined_l3cd, TRUE);
@@ -3019,6 +3059,12 @@ out:
         self->priv.p->combined_l3cd_commited =
             nm_l3_config_data_ref(self->priv.p->combined_l3cd_merged);
         commited_changed = TRUE;
+
+        _nm_l3cfg_emit_signal_notify_l3cd_changed(self,
+                                                  l3cd_commited_old,
+                                                  self->priv.p->combined_l3cd_commited,
+                                                  TRUE);
+
         NM_SET_OUT(out_old, g_steal_pointer(&l3cd_commited_old));
         NM_SET_OUT(out_changed_combined_l3cd, TRUE);
     }
@@ -3652,6 +3698,23 @@ _nm_l3cfg_unregister_ipv4ll(NML3Cfg *self)
 
 /*****************************************************************************/
 
+gboolean
+nm_l3cfg_is_ready(NML3Cfg *self)
+{
+    g_return_val_if_fail(NM_IS_L3CFG(self), FALSE);
+
+    if (self->priv.p->changed_configs_configs)
+        return FALSE;
+    if (self->priv.p->changed_configs_acd_state)
+        return FALSE;
+    if (self->priv.p->commit_on_idle_source)
+        return FALSE;
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+
 static void
 set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h
index 5cfc93f6..037f2178 100644
--- a/src/core/nm-l3cfg.h
+++ b/src/core/nm-l3cfg.h
@@ -3,7 +3,7 @@
 #ifndef __NM_L3CFG_H__
 #define __NM_L3CFG_H__
 
-#include "platform/nmp-object.h"
+#include "libnm-platform/nmp-object.h"
 #include "nm-l3-config-data.h"
 
 #define NM_L3CFG_CONFIG_PRIORITY_IPV4LL 0
@@ -88,11 +88,18 @@ nm_l3_acd_addr_info_find_track_info(const NML3AcdAddrInfo *addr_info,
 }
 
 typedef enum {
+    /* emitted when the merged/commited NML3ConfigData instance changes.
+     * Note that this gets emitted "under unsafe circumstances". That means,
+     * you should not perform complex operations inside this callback,
+     * and neither should you call into NML3Cfg again (reentrancy). */
+    NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED,
+
     NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED,
 
     NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT,
 
-    /* emitted at the end of nm_l3cfg_platform_commit(). */
+    /* emitted at the end of nm_l3cfg_platform_commit(). This signals also that
+     * nm_l3cfg_is_ready() might have switched to TRUE. */
     NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT,
 
     /* NML3Cfg hooks to the NMPlatform signals for link, addresses and routes.
@@ -118,6 +125,12 @@ typedef struct {
     NML3ConfigNotifyType notify_type;
     union {
         struct {
+            const NML3ConfigData *l3cd_old;
+            const NML3ConfigData *l3cd_new;
+            bool                  commited;
+        } l3cd_changed;
+
+        struct {
             NML3AcdAddrInfo info;
         } acd_event;
 
@@ -158,6 +171,8 @@ NML3Cfg *nm_l3cfg_new(NMNetns *netns, int ifindex);
 
 /*****************************************************************************/
 
+gboolean nm_l3cfg_is_ready(NML3Cfg *self);
+
 void _nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint32 obj_type_flags);
 
 void _nm_l3cfg_notify_platform_change(NML3Cfg *                  self,
@@ -173,6 +188,8 @@ struct _NMDedupMultiIndex *nm_netns_get_multi_idx(NMNetns *self);
 static inline struct _NMDedupMultiIndex *
 nm_l3cfg_get_multi_idx(const NML3Cfg *self)
 {
+    g_return_val_if_fail(NM_IS_L3CFG(self), NULL);
+
     return nm_netns_get_multi_idx(self->priv.netns);
 }
 
@@ -181,7 +198,7 @@ nm_l3cfg_get_multi_idx(const NML3Cfg *self)
 static inline int
 nm_l3cfg_get_ifindex(const NML3Cfg *self)
 {
-    nm_assert(NM_IS_L3CFG(self));
+    g_return_val_if_fail(NM_IS_L3CFG(self), 0);
 
     return self->priv.ifindex;
 }
@@ -314,7 +331,7 @@ typedef enum _nm_packed {
 
 void nm_l3cfg_commit(NML3Cfg *self, NML3CfgCommitType commit_type);
 
-void nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self);
+gboolean nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self);
 
 /*****************************************************************************/
 
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
index 5a6e05a9..b66f7fbb 100644
--- a/src/core/nm-manager.c
+++ b/src/core/nm-manager.c
@@ -16,15 +16,15 @@
 #include <sys/sendfile.h>
 #include <limits.h>
 
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-c-list.h"
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-dbus-manager.h"
 #include "vpn/nm-vpn-manager.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-generic.h"
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
 #include "nm-hostname-manager.h"
 #include "nm-keep-alive.h"
 #include "nm-rfkill-manager.h"
@@ -40,10 +40,10 @@
 #include "nm-policy.h"
 #include "nm-session-monitor.h"
 #include "nm-act-request.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-config.h"
 #include "nm-audit-manager.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 #include "nm-checkpoint.h"
 #include "nm-checkpoint-manager.h"
 #include "nm-dbus-object.h"
@@ -363,6 +363,7 @@ static NMActiveConnection *active_connection_find(NMManager *             self,
                                                   NMSettingsConnection *  sett_conn,
                                                   const char *            uuid,
                                                   NMActiveConnectionState max_state,
+                                                  gboolean                also_waiting_auth,
                                                   GPtrArray **            out_all_matching);
 
 static NMConnectivity *concheck_get_mgr(NMManager *self);
@@ -833,6 +834,7 @@ _delete_volatile_connection_do(NMManager *self, NMSettingsConnection *connection
                                connection,
                                NULL,
                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+                               TRUE,
                                NULL))
         return;
 
@@ -978,6 +980,7 @@ active_connection_find(
     NMSettingsConnection *  sett_conn,
     const char *            uuid,
     NMActiveConnectionState max_state /* candidates in state @max_state will be found */,
+    gboolean                also_waiting_auth /* return also ACs waiting authorization */,
     GPtrArray **            out_all_matching)
 {
     NMManagerPrivate *  priv = NM_MANAGER_GET_PRIVATE(self);
@@ -1017,11 +1020,14 @@ active_connection_find(
     if (!best_ac) {
         AsyncOpData *async_op_data;
 
+        if (!also_waiting_auth)
+            return NULL;
+
         c_list_for_each_entry (async_op_data, &priv->async_op_lst_head, async_op_lst) {
             NMSettingsConnection *ac_conn;
 
             ac      = async_op_data->ac_auth.active;
-            ac_conn = nm_active_connection_get_settings_connection(ac);
+            ac_conn = _nm_active_connection_get_settings_connection(ac);
             if (sett_conn && sett_conn != ac_conn)
                 continue;
             if (uuid && !nm_streq0(uuid, nm_settings_connection_get_uuid(ac_conn)))
@@ -1078,6 +1084,7 @@ active_connection_find_by_connection(NMManager *             self,
                                   sett_conn,
                                   sett_conn ? NULL : nm_connection_get_uuid(connection),
                                   max_state,
+                                  FALSE,
                                   out_all_matching);
 }
 
@@ -1112,6 +1119,7 @@ _get_activatable_connections_filter(NMSettings *          settings,
                                    sett_conn,
                                    NULL,
                                    NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+                                   FALSE,
                                    NULL);
 }
 
@@ -1305,7 +1313,7 @@ find_device_by_permanent_hw_addr(NMManager *self, const char *hwaddr)
     NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
     NMDevice *        device;
     const char *      device_addr;
-    guint8            hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX];
+    guint8            hwaddr_bin[_NM_UTILS_HWADDR_LEN_MAX];
     gsize             hwaddr_len;
 
     g_return_val_if_fail(hwaddr != NULL, NULL);
@@ -1991,16 +1999,16 @@ nm_manager_remove_device(NMManager *self, const char *ifname, NMDeviceType devic
 static NMDevice *
 system_create_virtual_device(NMManager *self, NMConnection *connection)
 {
-    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
-    NMDeviceFactory * factory;
-    gs_free NMSettingsConnection **connections = NULL;
-    guint                          i;
-    gs_free char *                 iface = NULL;
-    const char *                   parent_spec;
-    NMDevice *                     device = NULL, *parent = NULL;
-    NMDevice *                     dev_candidate;
-    GError *                       error = NULL;
-    NMLogLevel                     log_level;
+    NMManagerPrivate *           priv = NM_MANAGER_GET_PRIVATE(self);
+    NMDeviceFactory *            factory;
+    NMSettingsConnection *const *connections;
+    guint                        i;
+    gs_free char *               iface = NULL;
+    const char *                 parent_spec;
+    NMDevice *                   device = NULL, *parent = NULL;
+    NMDevice *                   dev_candidate;
+    GError *                     error = NULL;
+    NMLogLevel                   log_level;
 
     g_return_val_if_fail(NM_IS_MANAGER(self), NULL);
     g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL);
@@ -2083,13 +2091,7 @@ system_create_virtual_device(NMManager *self, NMConnection *connection)
     }
 
     /* Create backing resources if the device has any autoconnect connections */
-    connections = nm_settings_get_connections_clone(
-        priv->settings,
-        NULL,
-        NULL,
-        NULL,
-        nm_settings_connection_cmp_autoconnect_priority_p_with_data,
-        NULL);
+    connections = nm_settings_get_connections_sorted_by_autoconnect_priority(priv->settings, NULL);
     for (i = 0; connections[i]; i++) {
         NMConnection *       candidate = nm_settings_connection_get_connection(connections[i]);
         NMSettingConnection *s_con;
@@ -2128,19 +2130,13 @@ system_create_virtual_device(NMManager *self, NMConnection *connection)
 static void
 retry_connections_for_parent_device(NMManager *self, NMDevice *device)
 {
-    NMManagerPrivate *priv                     = NM_MANAGER_GET_PRIVATE(self);
-    gs_free NMSettingsConnection **connections = NULL;
-    guint                          i;
+    NMManagerPrivate *           priv = NM_MANAGER_GET_PRIVATE(self);
+    NMSettingsConnection *const *connections;
+    guint                        i;
 
     g_return_if_fail(device);
 
-    connections = nm_settings_get_connections_clone(
-        priv->settings,
-        NULL,
-        NULL,
-        NULL,
-        nm_settings_connection_cmp_autoconnect_priority_p_with_data,
-        NULL);
+    connections = nm_settings_get_connections_sorted_by_autoconnect_priority(priv->settings, NULL);
     for (i = 0; connections[i]; i++) {
         NMSettingsConnection *sett_conn  = connections[i];
         NMConnection *        connection = nm_settings_connection_get_connection(sett_conn);
@@ -2245,6 +2241,7 @@ connection_flags_changed(NMSettings *settings, NMSettingsConnection *connection,
                                connection,
                                NULL,
                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+                               FALSE,
                                NULL)) {
         /* the connection still has an active-connection. It will be purged
          * when the active connection(s) get(s) removed. */
@@ -2564,6 +2561,7 @@ new_activation_allowed_for_connection(NMManager *self, NMSettingsConnection *con
                                    connection,
                                    NULL,
                                    NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+                                   FALSE,
                                    NULL);
 }
 
@@ -2839,14 +2837,16 @@ recheck_assume_connection(NMManager *self, NMDevice *device)
     g_return_val_if_fail(NM_IS_DEVICE(device), FALSE);
 
     if (!nm_device_get_managed(device, FALSE)) {
-        nm_device_assume_state_reset(device);
+        /* If the device is only unmanaged by NM_UNMANAGED_PLATFORM_INIT,
+         * don't reset the state now but wait until it becomes managed. */
+        if (nm_device_get_unmanaged_flags(device, NM_UNMANAGED_ALL) != NM_UNMANAGED_PLATFORM_INIT)
+            nm_device_assume_state_reset(device);
         _LOG2D(LOGD_DEVICE, device, "assume: don't assume because %s", "not managed");
         return FALSE;
     }
 
     state = nm_device_get_state(device);
     if (state > NM_DEVICE_STATE_DISCONNECTED) {
-        nm_device_assume_state_reset(device);
         _LOG2D(LOGD_DEVICE,
                device,
                "assume: don't assume due to device state %s",
@@ -2892,15 +2892,16 @@ recheck_assume_connection(NMManager *self, NMDevice *device)
                                                        NM_SETTING_IP4_CONFIG_METHOD_AUTO,
                                                        NULL));
 
-                nm_settings_connection_update(sett_conn,
-                                              con2,
-                                              NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
-                                              NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
-                                              NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
-                                                  | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL,
-                                              NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
-                                              "assume-initrd",
-                                              NULL);
+                nm_settings_connection_update(
+                    sett_conn,
+                    con2,
+                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+                    NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+                    NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+                        | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL,
+                    NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET,
+                    "assume-initrd",
+                    NULL);
             }
         }
     }
@@ -3161,7 +3162,10 @@ _device_realize_finish(NMManager *self, NMDevice *device, const NMPlatformLink *
     nm_device_realize_finish(device, plink);
 
     if (!nm_device_get_managed(device, FALSE)) {
-        nm_device_assume_state_reset(device);
+        /* If the device is only unmanaged by NM_UNMANAGED_PLATFORM_INIT,
+         * don't reset the state now but wait until it becomes managed. */
+        if (nm_device_get_unmanaged_flags(device, NM_UNMANAGED_ALL) != NM_UNMANAGED_PLATFORM_INIT)
+            nm_device_assume_state_reset(device);
         return;
     }
 
@@ -3510,6 +3514,45 @@ typedef struct {
 } PlatformLinkCbData;
 
 static gboolean
+_check_remove_dev_on_link_deleted(NMManager *self, NMDevice *device)
+{
+    NMManagerPrivate *           priv  = NM_MANAGER_GET_PRIVATE(self);
+    NMSettingsConnection *const *scons = NULL;
+    NMConnection *               con;
+    guint                        i;
+
+    nm_assert(nm_device_is_software(device));
+
+    /* In general, software devices stick around as unrealized
+     * until their connection is removed. However, we don't want
+     * that a NM-generated connection keeps the device alive.
+     * If there are no other compatible connections, the device
+     * should be also removed.
+     */
+
+    scons = nm_settings_get_connections(priv->settings, NULL);
+
+    for (i = 0; scons[i]; i++) {
+        con = nm_settings_connection_get_connection(scons[i]);
+        if (!nm_connection_is_virtual(con))
+            continue;
+
+        if (NM_FLAGS_HAS(nm_settings_connection_get_flags(scons[i]),
+                         NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
+            continue;
+
+        if (!nm_device_check_connection_compatible(device, con, NULL))
+            continue;
+
+        /* Found a virtual connection compatible, the device must
+         * stay around unrealized. */
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
 _platform_link_cb_idle(PlatformLinkCbData *data)
 {
     int                   ifindex = data->ifindex;
@@ -3534,13 +3577,15 @@ _platform_link_cb_idle(PlatformLinkCbData *data)
         if (device) {
             if (nm_device_is_software(device)) {
                 nm_device_sys_iface_state_set(device, NM_DEVICE_SYS_IFACE_STATE_REMOVED);
-                /* Our software devices stick around until their connection is removed */
                 if (!nm_device_unrealize(device, FALSE, &error)) {
                     _LOG2W(LOGD_DEVICE, device, "failed to unrealize: %s", error->message);
                     g_clear_error(&error);
                     remove_device(self, device, FALSE);
                 } else {
-                    nm_device_update_from_platform_link(device, NULL);
+                    if (_check_remove_dev_on_link_deleted(self, device))
+                        remove_device(self, device, FALSE);
+                    else
+                        nm_device_update_from_platform_link(device, NULL);
                 }
             } else {
                 /* Hardware and external devices always get removed when their kernel link is gone */
@@ -4134,6 +4179,7 @@ find_master(NMManager *            self,
                                                 master_connection,
                                                 NULL,
                                                 NM_ACTIVE_CONNECTION_STATE_DEACTIVATING,
+                                                FALSE,
                                                 NULL);
     }
 
@@ -4367,13 +4413,13 @@ find_slaves(NMManager *           manager,
             guint *               out_n_slaves,
             gboolean              for_user_request)
 {
-    NMManagerPrivate *priv                         = NM_MANAGER_GET_PRIVATE(manager);
-    gs_free NMSettingsConnection **all_connections = NULL;
-    guint                          n_all_connections;
-    guint                          i;
-    SlaveConnectionInfo *          slaves   = NULL;
-    guint                          n_slaves = 0;
-    NMSettingConnection *          s_con;
+    NMManagerPrivate *           priv            = NM_MANAGER_GET_PRIVATE(manager);
+    NMSettingsConnection *const *all_connections = NULL;
+    guint                        n_all_connections;
+    guint                        i;
+    SlaveConnectionInfo *        slaves   = NULL;
+    guint                        n_slaves = 0;
+    NMSettingConnection *        s_con;
     gs_unref_hashtable GHashTable *devices = NULL;
 
     nm_assert(out_n_slaves);
@@ -4387,13 +4433,9 @@ find_slaves(NMManager *           manager,
      * even if a slave was already active, it might be deactivated during
      * master reactivation.
      */
-    all_connections = nm_settings_get_connections_clone(
-        priv->settings,
-        &n_all_connections,
-        NULL,
-        NULL,
-        nm_settings_connection_cmp_autoconnect_priority_p_with_data,
-        NULL);
+    all_connections =
+        nm_settings_get_connections_sorted_by_autoconnect_priority(priv->settings,
+                                                                   &n_all_connections);
     for (i = 0; i < n_all_connections; i++) {
         NMSettingsConnection *master_connection = NULL;
         NMDevice *            master_device     = NULL, *slave_device;
@@ -4985,6 +5027,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
                                     sett_conn,
                                     NULL,
                                     NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+                                    FALSE,
                                     &all_ac_arr);
         if (ac) {
             n_all = all_ac_arr ? all_ac_arr->len : ((guint) 1);
@@ -5637,7 +5680,7 @@ activation_add_done(NMSettings *           settings,
         result_floating = g_variant_new("(oo@a{sv})",
                                         nm_dbus_object_get_path(NM_DBUS_OBJECT(new_connection)),
                                         nm_dbus_object_get_path(NM_DBUS_OBJECT(active)),
-                                        g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0));
+                                        nm_g_variant_singleton_aLsvI());
     }
     g_dbus_method_invocation_return_value(context, result_floating);
 
@@ -6859,9 +6902,9 @@ devices_inited_cb(gpointer user_data)
 gboolean
 nm_manager_start(NMManager *self, GError **error)
 {
-    NMManagerPrivate *priv                     = NM_MANAGER_GET_PRIVATE(self);
-    gs_free NMSettingsConnection **connections = NULL;
-    guint                          i;
+    NMManagerPrivate *           priv = NM_MANAGER_GET_PRIVATE(self);
+    NMSettingsConnection *const *connections;
+    guint                        i;
 
     nm_device_factory_manager_load_factories(_register_device_factory, self);
 
@@ -6919,13 +6962,7 @@ nm_manager_start(NMManager *self, GError **error)
                      NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
                      G_CALLBACK(connection_updated_cb),
                      self);
-    connections = nm_settings_get_connections_clone(
-        priv->settings,
-        NULL,
-        NULL,
-        NULL,
-        nm_settings_connection_cmp_autoconnect_priority_p_with_data,
-        NULL);
+    connections = nm_settings_get_connections_sorted_by_autoconnect_priority(priv->settings, NULL);
     for (i = 0; connections[i]; i++)
         connection_changed(self, connections[i]);
 
@@ -7102,6 +7139,85 @@ typedef struct {
 
 #define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied"
 
+static const char *
+_dbus_set_property_audit_log_get_args(NMDBusObject *obj,
+                                      const char *  property_name,
+                                      GVariant *    value,
+                                      char **       str_to_free)
+{
+    nm_assert(str_to_free && !*str_to_free);
+
+    /* We assert here that the property is one of the few expected ones.
+     *
+     * Future properties should not made writable! Add a D-Bus method instead,
+     * they are more flexible (for example, you can set multiple properties at
+     * once). */
+
+    if (NM_IS_DEVICE(obj)) {
+        nm_assert(NM_IN_STRSET(property_name,
+                               NM_DEVICE_MANAGED,
+                               NM_DEVICE_AUTOCONNECT,
+                               NM_DEVICE_STATISTICS_REFRESH_RATE_MS));
+        return (*str_to_free = g_variant_print(value, FALSE));
+    }
+
+    nm_assert(NM_IS_MANAGER(obj));
+    if (NM_IN_STRSET(property_name,
+                     NM_MANAGER_WIRELESS_ENABLED,
+                     NM_MANAGER_WWAN_ENABLED,
+                     NM_MANAGER_WIMAX_ENABLED,
+                     NM_MANAGER_CONNECTIVITY_CHECK_ENABLED)) {
+        return (*str_to_free = g_strdup_printf("%s:%s",
+                                               property_name,
+                                               g_variant_get_boolean(value) ? "on" : "off"));
+    }
+    if (NM_IN_STRSET(property_name, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) {
+        return NM_MANAGER_GLOBAL_DNS_CONFIGURATION;
+    }
+
+    return nm_assert_unreachable_val("???");
+}
+
+/* this is a macro to catch the caller's line number. */
+#define _dbus_set_property_audit_log(obj,                                                  \
+                                     audit_op,                                             \
+                                     auth_subject,                                         \
+                                     property_name,                                        \
+                                     value,                                                \
+                                     error_message)                                        \
+    G_STMT_START                                                                           \
+    {                                                                                      \
+        NMDBusObject *const  _obj           = (obj);                                       \
+        const char *const    _audit_op      = (audit_op);                                  \
+        NMAuthSubject *const _auth_subject  = (auth_subject);                              \
+        const char *const    _property_name = (property_name);                             \
+        GVariant *const      _value         = (value);                                     \
+        const char *const    _error_message = (error_message);                             \
+        gs_free char *       _args_to_free  = NULL;                                        \
+                                                                                           \
+        if (NM_IS_DEVICE(_obj)) {                                                          \
+            nm_audit_log_device_op(_audit_op,                                              \
+                                   NM_DEVICE(_obj),                                        \
+                                   !_error_message,                                        \
+                                   _dbus_set_property_audit_log_get_args(_obj,             \
+                                                                         _property_name,   \
+                                                                         _value,           \
+                                                                         &_args_to_free),  \
+                                   _auth_subject,                                          \
+                                   _error_message);                                        \
+        } else {                                                                           \
+            nm_audit_log_control_op(_audit_op,                                             \
+                                    _dbus_set_property_audit_log_get_args(_obj,            \
+                                                                          _property_name,  \
+                                                                          _value,          \
+                                                                          &_args_to_free), \
+                                    !_error_message,                                       \
+                                    _auth_subject,                                         \
+                                    _error_message);                                       \
+        }                                                                                  \
+    }                                                                                      \
+    G_STMT_END
+
 static void
 _dbus_set_property_auth_cb(NMAuthChain *          chain,
                            GDBusMethodInvocation *invocation,
@@ -7159,11 +7275,13 @@ _dbus_set_property_auth_cb(NMAuthChain *          chain,
     g_value_unset(&gvalue);
 
 out:
-    nm_audit_log_control_op(property_info->writable.audit_op,
-                            property_info->property_name,
-                            !error_message,
-                            nm_auth_chain_get_subject(chain),
-                            error_message);
+    _dbus_set_property_audit_log(obj,
+                                 property_info->writable.audit_op,
+                                 nm_auth_chain_get_subject(chain),
+                                 property_info->property_name,
+                                 value,
+                                 error_message);
+
     if (error_message)
         g_dbus_method_invocation_return_dbus_error(invocation, error_name, error_message);
     else
@@ -7187,10 +7305,27 @@ nm_manager_dbus_set_property_handle(NMDBusObject *                     obj,
     gs_unref_object NMAuthSubject *subject = NULL;
     DBusSetPropertyHandle *        handle_data;
 
+    /* we only have writable properties on Device or Manager. In the future,
+     * we probably should not add new API with writable properties. Add
+     * methods instead. Systemd also avoids writable properties. */
+    nm_assert(obj == (gpointer) self || NM_IS_DEVICE(obj));
+
     subject = nm_dbus_manager_new_auth_subject_from_context(invocation);
     if (!subject) {
         error_message = NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN;
-        goto err;
+
+        _dbus_set_property_audit_log(obj,
+                                     property_info->writable.audit_op,
+                                     NULL,
+                                     property_info->property_name,
+                                     value,
+                                     error_message);
+
+        g_dbus_method_invocation_return_error_literal(invocation,
+                                                      G_DBUS_ERROR,
+                                                      G_DBUS_ERROR_AUTH_FAILED,
+                                                      error_message);
+        return;
     }
 
     handle_data                    = g_slice_new0(DBusSetPropertyHandle);
@@ -7204,18 +7339,6 @@ nm_manager_dbus_set_property_handle(NMDBusObject *                     obj,
     chain = nm_auth_chain_new_subject(subject, invocation, _dbus_set_property_auth_cb, handle_data);
     c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain));
     nm_auth_chain_add_call_unsafe(chain, property_info->writable.permission, TRUE);
-    return;
-
-err:
-    nm_audit_log_control_op(property_info->writable.audit_op,
-                            property_info->property_name,
-                            FALSE,
-                            invocation,
-                            error_message);
-    g_dbus_method_invocation_return_error_literal(invocation,
-                                                  G_DBUS_ERROR,
-                                                  G_DBUS_ERROR_AUTH_FAILED,
-                                                  error_message);
 }
 
 /*****************************************************************************/
@@ -7854,10 +7977,8 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         break;
     case PROP_CAPABILITIES:
         g_value_set_variant(value,
-                            g_variant_new_fixed_array(G_VARIANT_TYPE("u"),
-                                                      priv->capabilities->data,
-                                                      priv->capabilities->len,
-                                                      sizeof(guint32)));
+                            nm_g_variant_new_au((const guint32 *) priv->capabilities->data,
+                                                priv->capabilities->len));
         break;
     case PROP_STATE:
         g_value_set_uint(value, priv->state);
@@ -8297,76 +8418,74 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
                         NM_DEFINE_GDBUS_ARG_INFO("checkpoint", "o"),
                         NM_DEFINE_GDBUS_ARG_INFO("add_timeout", "u"), ), ),
                 .handle = impl_manager_checkpoint_adjust_rollback_timeout, ), ),
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_check_permissions,
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_check_permissions,
                                                 &signal_info_state_changed,
                                                 &signal_info_device_added,
                                                 &signal_info_device_removed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Devices", "ao", NM_MANAGER_DEVICES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("AllDevices",
-                                                             "ao",
-                                                             NM_MANAGER_ALL_DEVICES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Checkpoints",
-                                                             "ao",
-                                                             NM_MANAGER_CHECKPOINTS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("NetworkingEnabled",
-                                                             "b",
-                                                             NM_MANAGER_NETWORKING_ENABLED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Devices", "ao", NM_MANAGER_DEVICES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AllDevices",
+                                                           "ao",
+                                                           NM_MANAGER_ALL_DEVICES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Checkpoints",
+                                                           "ao",
+                                                           NM_MANAGER_CHECKPOINTS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NetworkingEnabled",
+                                                           "b",
+                                                           NM_MANAGER_NETWORKING_ENABLED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(
                 "WirelessEnabled",
                 "b",
                 NM_MANAGER_WIRELESS_ENABLED,
                 NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI,
                 NM_AUDIT_OP_RADIO_CONTROL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WirelessHardwareEnabled",
-                                                             "b",
-                                                             NM_MANAGER_WIRELESS_HARDWARE_ENABLED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WirelessHardwareEnabled",
+                                                           "b",
+                                                           NM_MANAGER_WIRELESS_HARDWARE_ENABLED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(
                 "WwanEnabled",
                 "b",
                 NM_MANAGER_WWAN_ENABLED,
                 NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN,
                 NM_AUDIT_OP_RADIO_CONTROL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WwanHardwareEnabled",
-                                                             "b",
-                                                             NM_MANAGER_WWAN_HARDWARE_ENABLED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WwanHardwareEnabled",
+                                                           "b",
+                                                           NM_MANAGER_WWAN_HARDWARE_ENABLED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(
                 "WimaxEnabled",
                 "b",
                 NM_MANAGER_WIMAX_ENABLED,
                 NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX,
                 NM_AUDIT_OP_RADIO_CONTROL),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WimaxHardwareEnabled",
-                                                             "b",
-                                                             NM_MANAGER_WIMAX_HARDWARE_ENABLED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ActiveConnections",
-                                                             "ao",
-                                                             NM_MANAGER_ACTIVE_CONNECTIONS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("PrimaryConnection",
-                                                             "o",
-                                                             NM_MANAGER_PRIMARY_CONNECTION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("PrimaryConnectionType",
-                                                             "s",
-                                                             NM_MANAGER_PRIMARY_CONNECTION_TYPE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Metered", "u", NM_MANAGER_METERED),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ActivatingConnection",
-                                                             "o",
-                                                             NM_MANAGER_ACTIVATING_CONNECTION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Startup", "b", NM_MANAGER_STARTUP),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Version", "s", NM_MANAGER_VERSION),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Capabilities",
-                                                             "au",
-                                                             NM_MANAGER_CAPABILITIES),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("State", "u", NM_MANAGER_STATE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Connectivity",
-                                                             "u",
-                                                             NM_MANAGER_CONNECTIVITY),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(
-                "ConnectivityCheckAvailable",
-                "b",
-                NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WimaxHardwareEnabled",
+                                                           "b",
+                                                           NM_MANAGER_WIMAX_HARDWARE_ENABLED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ActiveConnections",
+                                                           "ao",
+                                                           NM_MANAGER_ACTIVE_CONNECTIONS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("PrimaryConnection",
+                                                           "o",
+                                                           NM_MANAGER_PRIMARY_CONNECTION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("PrimaryConnectionType",
+                                                           "s",
+                                                           NM_MANAGER_PRIMARY_CONNECTION_TYPE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Metered", "u", NM_MANAGER_METERED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ActivatingConnection",
+                                                           "o",
+                                                           NM_MANAGER_ACTIVATING_CONNECTION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Startup", "b", NM_MANAGER_STARTUP),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Version", "s", NM_MANAGER_VERSION),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Capabilities",
+                                                           "au",
+                                                           NM_MANAGER_CAPABILITIES),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("State", "u", NM_MANAGER_STATE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Connectivity",
+                                                           "u",
+                                                           NM_MANAGER_CONNECTIVITY),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ConnectivityCheckAvailable",
+                                                           "b",
+                                                           NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(
                 "ConnectivityCheckEnabled",
                 "b",
                 NM_MANAGER_CONNECTIVITY_CHECK_ENABLED,
@@ -8375,13 +8494,12 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ConnectivityCheckUri",
                                                            "s",
                                                            NM_MANAGER_CONNECTIVITY_CHECK_URI),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(
                 "GlobalDnsConfiguration",
                 "a{sv}",
                 NM_MANAGER_GLOBAL_DNS_CONFIGURATION,
                 NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS,
                 NM_AUDIT_OP_NET_CONTROL), ), ),
-    .legacy_property_changed = TRUE,
 };
 
 static void
diff --git a/src/core/nm-netns.c b/src/core/nm-netns.c
index 88ea09b6..d5a0317d 100644
--- a/src/core/nm-netns.c
+++ b/src/core/nm-netns.c
@@ -7,15 +7,15 @@
 
 #include "nm-netns.h"
 
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-c-list.h"
 
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-l3cfg.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nmp-netns.h"
-#include "platform/nmp-rules-manager.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-netns.h"
+#include "libnm-platform/nmp-rules-manager.h"
 
 /*****************************************************************************/
 
@@ -263,7 +263,7 @@ nm_netns_shared_ip_reserve(NMNetns *self)
 
     if (!priv->shared_ips) {
         addr             = addr_start;
-        priv->shared_ips = g_hash_table_new(nm_puint32_hash, nm_puint32_equals);
+        priv->shared_ips = g_hash_table_new(nm_puint32_hash, nm_puint32_equal);
         g_object_ref(self);
     } else {
         guint32 count;
@@ -392,7 +392,7 @@ constructed(GObject *object)
     if (!priv->platform)
         g_return_if_reached();
 
-    priv->l3cfgs = g_hash_table_new_full(nm_pint_hash, nm_pint_equals, _l3cfg_data_free, NULL);
+    priv->l3cfgs = g_hash_table_new_full(nm_pint_hash, nm_pint_equal, _l3cfg_data_free, NULL);
 
     priv->platform_netns = nm_platform_netns_get(priv->platform);
 
diff --git a/src/core/nm-netns.h b/src/core/nm-netns.h
index aab3b520..78a9121a 100644
--- a/src/core/nm-netns.h
+++ b/src/core/nm-netns.h
@@ -6,7 +6,7 @@
 #ifndef __NM_NETNS_H__
 #define __NM_NETNS_H__
 
-#include "nm-platform/nmp-base.h"
+#include "libnm-platform/nmp-base.h"
 
 #define NM_TYPE_NETNS            (nm_netns_get_type())
 #define NM_NETNS(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_NETNS, NMNetns))
@@ -19,13 +19,15 @@
 
 typedef struct _NMNetnsClass NMNetnsClass;
 
+struct _NMPlatform;
+
 GType nm_netns_get_type(void);
 
 NMNetns *nm_netns_get(void);
-NMNetns *nm_netns_new(NMPlatform *platform);
+NMNetns *nm_netns_new(struct _NMPlatform *platform);
 
-NMPlatform *nm_netns_get_platform(NMNetns *self);
-NMPNetns *  nm_netns_get_platform_netns(NMNetns *self);
+struct _NMPlatform *nm_netns_get_platform(NMNetns *self);
+NMPNetns *          nm_netns_get_platform_netns(NMNetns *self);
 
 struct _NMPRulesManager *nm_netns_get_rules_manager(NMNetns *self);
 
diff --git a/src/core/nm-pacrunner-manager.c b/src/core/nm-pacrunner-manager.c
index b58aef06..67930dba 100644
--- a/src/core/nm-pacrunner-manager.c
+++ b/src/core/nm-pacrunner-manager.c
@@ -9,13 +9,13 @@
 
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-dbus-manager.h"
 #include "nm-proxy-config.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 #include "c-list/src/c-list.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
 
 #define PACRUNNER_DBUS_SERVICE   "org.pacrunner"
 #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager"
@@ -198,6 +198,7 @@ _make_request_create_proxy_configuration(NMProxyConfig *proxy_config,
             g_variant_builder_add(&builder, "{sv}", "Script", g_variant_new_string(pac_script));
         }
         break;
+    case NM_PROXY_CONFIG_METHOD_UNKNOWN:
     case NM_PROXY_CONFIG_METHOD_NONE:
         g_variant_builder_add(&builder, "{sv}", "Method", g_variant_new_string("direct"));
         break;
diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c
index db4983f8..e4914acf 100644
--- a/src/core/nm-policy.c
+++ b/src/core/nm-policy.c
@@ -17,14 +17,14 @@
 #include "devices/nm-device.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-connection.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "dns/nm-dns-manager.h"
 #include "vpn/nm-vpn-manager.h"
 #include "nm-auth-utils.h"
-#include "nm-firewall-manager.h"
+#include "nm-firewalld-manager.h"
 #include "nm-dispatcher.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-manager.h"
 #include "settings/nm-settings.h"
 #include "settings/nm-settings-connection.h"
@@ -45,10 +45,10 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMPolicy,
                              PROP_ACTIVATING_IP6_AC, );
 
 typedef struct {
-    NMManager *        manager;
-    NMNetns *          netns;
-    NMFirewallManager *firewall_manager;
-    CList              pending_activation_checks;
+    NMManager *         manager;
+    NMNetns *           netns;
+    NMFirewalldManager *firewalld_manager;
+    CList               pending_activation_checks;
 
     NMAgentManager *agent_mgr;
 
@@ -652,6 +652,7 @@ device_hostname_info_compare(gconstpointer a, gconstpointer b)
 
     NM_CMP_FIELD(info1, info2, priority);
     NM_CMP_FIELD_UNSAFE(info2, info1, is_default);
+    NM_CMP_FIELD_UNSAFE(info2, info1, IS_IPv4);
 
     return 0;
 }
@@ -2003,19 +2004,12 @@ device_state_changed(NMDevice *          device,
                           nm_settings_connection_get_id(sett_conn));
                 }
             }
-
-            nm_settings_connection_clear_secrets(sett_conn, FALSE, FALSE);
         }
         break;
     case NM_DEVICE_STATE_ACTIVATED:
         if (sett_conn) {
             /* Reset auto retries back to default since connection was successful */
             nm_settings_connection_autoconnect_retries_reset(sett_conn);
-
-            /* And clear secrets so they will always be requested from the
-             * settings service when the next connection is made.
-             */
-            nm_settings_connection_clear_secrets(sett_conn, FALSE, FALSE);
         }
 
         /* Add device's new IPv4 and IPv6 configs to DNS */
@@ -2525,7 +2519,7 @@ connection_added(NMSettings *settings, NMSettingsConnection *connection, gpointe
 }
 
 static void
-firewall_state_changed(NMFirewallManager *manager, gboolean initialized_now, gpointer user_data)
+firewall_state_changed(NMFirewalldManager *manager, gboolean initialized_now, gpointer user_data)
 {
     NMPolicy *       self = (NMPolicy *) user_data;
     NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
@@ -2539,7 +2533,7 @@ firewall_state_changed(NMFirewallManager *manager, gboolean initialized_now, gpo
         return;
     }
 
-    if (!nm_firewall_manager_get_running(manager))
+    if (!nm_firewalld_manager_get_running(manager))
         return;
 
     /* add interface of each device to correct zone */
@@ -2791,9 +2785,9 @@ constructed(GObject *object)
 
     priv->agent_mgr = g_object_ref(nm_agent_manager_get());
 
-    priv->firewall_manager = g_object_ref(nm_firewall_manager_get());
-    g_signal_connect(priv->firewall_manager,
-                     NM_FIREWALL_MANAGER_STATE_CHANGED,
+    priv->firewalld_manager = g_object_ref(nm_firewalld_manager_get());
+    g_signal_connect(priv->firewalld_manager,
+                     NM_FIREWALLD_MANAGER_STATE_CHANGED,
                      G_CALLBACK(firewall_state_changed),
                      self);
 
@@ -2896,9 +2890,9 @@ dispose(GObject *object)
     g_slist_free_full(priv->pending_secondaries, (GDestroyNotify) pending_secondary_data_free);
     priv->pending_secondaries = NULL;
 
-    if (priv->firewall_manager) {
-        g_signal_handlers_disconnect_by_func(priv->firewall_manager, firewall_state_changed, self);
-        g_clear_object(&priv->firewall_manager);
+    if (priv->firewalld_manager) {
+        g_signal_handlers_disconnect_by_func(priv->firewalld_manager, firewall_state_changed, self);
+        g_clear_object(&priv->firewalld_manager);
     }
 
     if (priv->agent_mgr) {
diff --git a/src/core/nm-proxy-config.c b/src/core/nm-proxy-config.c
index f41601c9..49156dfa 100644
--- a/src/core/nm-proxy-config.c
+++ b/src/core/nm-proxy-config.c
@@ -9,7 +9,7 @@
 
 #include <stdlib.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/nm-proxy-config.h b/src/core/nm-proxy-config.h
index 7aa587d0..7b6241f8 100644
--- a/src/core/nm-proxy-config.h
+++ b/src/core/nm-proxy-config.h
@@ -7,8 +7,7 @@
 #define __NETWORKMANAGER_PROXY_CONFIG_H__
 
 #include "nm-setting-proxy.h"
-
-typedef enum { NM_PROXY_CONFIG_METHOD_AUTO = 0, NM_PROXY_CONFIG_METHOD_NONE } NMProxyConfigMethod;
+#include "nm-l3-config-data.h"
 
 #define NM_TYPE_PROXY_CONFIG (nm_proxy_config_get_type())
 #define NM_PROXY_CONFIG(obj) \
diff --git a/src/core/nm-rfkill-manager.c b/src/core/nm-rfkill-manager.c
index f032851e..7f0b7726 100644
--- a/src/core/nm-rfkill-manager.c
+++ b/src/core/nm-rfkill-manager.c
@@ -9,7 +9,7 @@
 
 #include <libudev.h>
 
-#include "nm-udev-aux/nm-udev-utils.h"
+#include "libnm-udev-aux/nm-udev-utils.h"
 
 /*****************************************************************************/
 
diff --git a/src/core/nm-sleep-monitor.c b/src/core/nm-sleep-monitor.c
index ffb4db4e..306453e7 100644
--- a/src/core/nm-sleep-monitor.c
+++ b/src/core/nm-sleep-monitor.c
@@ -11,7 +11,7 @@
 #include <sys/stat.h>
 #include <gio/gunixfdlist.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 
 #if defined(SUSPEND_RESUME_UPOWER)
@@ -108,7 +108,7 @@ upower_resuming_cb(GDBusProxy *proxy, gpointer user_data)
     sleep_signal(user_data, FALSE);
 }
 
-#else /* USE_UPOWER */
+#else  /* USE_UPOWER */
 
 static void
 drop_inhibitor(NMSleepMonitor *self, gboolean force)
diff --git a/src/core/nm-test-utils-core.h b/src/core/nm-test-utils-core.h
index e655ce22..99d12ba9 100644
--- a/src/core/nm-test-utils-core.h
+++ b/src/core/nm-test-utils-core.h
@@ -7,41 +7,11 @@
 #define __NM_TEST_UTILS_CORE_H__
 
 #include "NetworkManagerUtils.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 
 #define _NMTST_INSIDE_CORE 1
 
-#include "nm-utils/nm-test-utils.h"
-
-/*****************************************************************************/
-
-#define NMTST_EXPECT_NM(level, msg) NMTST_EXPECT("NetworkManager", level, msg)
-
-#define NMTST_EXPECT_NM_ERROR(msg) NMTST_EXPECT_NM(G_LOG_LEVEL_MESSAGE, "*<error> [*] " msg)
-#define NMTST_EXPECT_NM_WARN(msg)  NMTST_EXPECT_NM(G_LOG_LEVEL_MESSAGE, "*<warn>  [*] " msg)
-#define NMTST_EXPECT_NM_INFO(msg)  NMTST_EXPECT_NM(G_LOG_LEVEL_INFO, "*<info>  [*] " msg)
-#define NMTST_EXPECT_NM_DEBUG(msg) NMTST_EXPECT_NM(G_LOG_LEVEL_DEBUG, "*<debug> [*] " msg)
-#define NMTST_EXPECT_NM_TRACE(msg) NMTST_EXPECT_NM(G_LOG_LEVEL_DEBUG, "*<trace> [*] " msg)
-
-static inline void
-nmtst_init_with_logging(int *argc, char ***argv, const char *log_level, const char *log_domains)
-{
-    __nmtst_init(argc, argv, FALSE, log_level, log_domains, NULL);
-}
-static inline void
-nmtst_init_assert_logging(int *argc, char ***argv, const char *log_level, const char *log_domains)
-{
-    gboolean set_logging;
-
-    __nmtst_init(argc, argv, TRUE, NULL, NULL, &set_logging);
-
-    if (!set_logging) {
-        gboolean success;
-
-        success = nm_logging_setup(log_level, log_domains, NULL, NULL);
-        g_assert(success);
-    }
-}
+#include "libnm-glib-aux/nm-test-utils.h"
 
 /*****************************************************************************/
 
@@ -347,7 +317,7 @@ nmtst_platform_ip6_routes_equal_aptr(const NMPObject *const *  a,
 
 #ifdef __NETWORKMANAGER_IP4_CONFIG_H__
 
-    #include "nm-glib-aux/nm-dedup-multi.h"
+    #include "libnm-glib-aux/nm-dedup-multi.h"
 
 static inline NMIP4Config *
 nmtst_ip4_config_new(int ifindex)
@@ -361,7 +331,7 @@ nmtst_ip4_config_new(int ifindex)
 
 #ifdef __NETWORKMANAGER_IP6_CONFIG_H__
 
-    #include "nm-glib-aux/nm-dedup-multi.h"
+    #include "libnm-glib-aux/nm-dedup-multi.h"
 
 static inline NMIP6Config *
 nmtst_ip6_config_new(int ifindex)
diff --git a/src/core/nm-types.h b/src/core/nm-types.h
index ab231459..7ad5aee1 100644
--- a/src/core/nm-types.h
+++ b/src/core/nm-types.h
@@ -10,8 +10,6 @@
     #error Cannot use this header.
 #endif
 
-#define _NM_SD_MAX_CLIENT_ID_LEN (sizeof(guint32) + 128)
-
 /* core */
 typedef struct _NMDBusObject            NMDBusObject;
 typedef struct _NMActiveConnection      NMActiveConnection;
@@ -77,147 +75,6 @@ typedef enum {
     NM_ACTIVATION_REASON_USER_REQUEST,
 } NMActivationReason;
 
-/* platform */
-typedef struct _NMPlatform               NMPlatform;
-typedef struct _NMPlatformObject         NMPlatformObject;
-typedef struct _NMPlatformObjWithIfindex NMPlatformObjWithIfindex;
-typedef struct _NMPlatformIP4Address     NMPlatformIP4Address;
-typedef struct _NMPlatformIP4Route       NMPlatformIP4Route;
-typedef struct _NMPlatformIP6Address     NMPlatformIP6Address;
-typedef struct _NMPlatformIP6Route       NMPlatformIP6Route;
-typedef struct _NMPlatformLink           NMPlatformLink;
-typedef struct _NMPObject                NMPObject;
-
-typedef enum {
-
-    /* No type, used as error value */
-    NM_LINK_TYPE_NONE,
-
-    NM_LINK_TYPE_UNKNOWN,
-
-    NM_LINK_TYPE_ANY,
-
-#define _NM_LINK_TYPE_REAL_FIRST NM_LINK_TYPE_ETHERNET
-
-/* Hardware types */
-#define _NM_LINK_TYPE_HW_FIRST NM_LINK_TYPE_ETHERNET
-    NM_LINK_TYPE_ETHERNET,
-    NM_LINK_TYPE_INFINIBAND,
-    NM_LINK_TYPE_OLPC_MESH,
-    NM_LINK_TYPE_WIFI,
-    NM_LINK_TYPE_WWAN_NET, /* WWAN kernel netdevice */
-    NM_LINK_TYPE_WIMAX,
-    NM_LINK_TYPE_WPAN,
-    NM_LINK_TYPE_6LOWPAN,
-    NM_LINK_TYPE_WIFI_P2P,
-#define _NM_LINK_TYPE_HW_LAST NM_LINK_TYPE_WIFI_P2P
-
-/* Software types */
-#define _NM_LINK_TYPE_SW_FIRST NM_LINK_TYPE_BNEP
-    NM_LINK_TYPE_BNEP, /* Bluetooth Ethernet emulation */
-    NM_LINK_TYPE_DUMMY,
-    NM_LINK_TYPE_GRE,
-    NM_LINK_TYPE_GRETAP,
-    NM_LINK_TYPE_IFB,
-    NM_LINK_TYPE_IP6TNL,
-    NM_LINK_TYPE_IP6GRE,
-    NM_LINK_TYPE_IP6GRETAP,
-    NM_LINK_TYPE_IPIP,
-    NM_LINK_TYPE_LOOPBACK,
-    NM_LINK_TYPE_MACSEC,
-    NM_LINK_TYPE_MACVLAN,
-    NM_LINK_TYPE_MACVTAP,
-    NM_LINK_TYPE_OPENVSWITCH,
-    NM_LINK_TYPE_PPP,
-    NM_LINK_TYPE_SIT,
-    NM_LINK_TYPE_TUN,
-    NM_LINK_TYPE_VETH,
-    NM_LINK_TYPE_VLAN,
-    NM_LINK_TYPE_VRF,
-    NM_LINK_TYPE_VXLAN,
-    NM_LINK_TYPE_WIREGUARD,
-#define _NM_LINK_TYPE_SW_LAST NM_LINK_TYPE_WIREGUARD
-
-/* Software types with slaves */
-#define _NM_LINK_TYPE_SW_MASTER_FIRST NM_LINK_TYPE_BRIDGE
-    NM_LINK_TYPE_BRIDGE,
-    NM_LINK_TYPE_BOND,
-    NM_LINK_TYPE_TEAM,
-#define _NM_LINK_TYPE_SW_MASTER_LAST NM_LINK_TYPE_TEAM
-
-#define _NM_LINK_TYPE_REAL_LAST NM_LINK_TYPE_TEAM
-
-#define _NM_LINK_TYPE_REAL_NUM ((int) (_NM_LINK_TYPE_REAL_LAST - _NM_LINK_TYPE_REAL_FIRST + 1))
-
-} NMLinkType;
-
-static inline gboolean
-nm_link_type_is_software(NMLinkType link_type)
-{
-    G_STATIC_ASSERT(_NM_LINK_TYPE_SW_LAST + 1 == _NM_LINK_TYPE_SW_MASTER_FIRST);
-
-    return link_type >= _NM_LINK_TYPE_SW_FIRST && link_type <= _NM_LINK_TYPE_SW_MASTER_LAST;
-}
-
-static inline gboolean
-nm_link_type_supports_slaves(NMLinkType link_type)
-{
-    return link_type >= _NM_LINK_TYPE_SW_MASTER_FIRST && link_type <= _NM_LINK_TYPE_SW_MASTER_LAST;
-}
-
-typedef enum {
-    NMP_OBJECT_TYPE_UNKNOWN,
-    NMP_OBJECT_TYPE_LINK,
-
-#define NMP_OBJECT_TYPE_IP_ADDRESS(is_ipv4) \
-    ((is_ipv4) ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS)
-    NMP_OBJECT_TYPE_IP4_ADDRESS,
-    NMP_OBJECT_TYPE_IP6_ADDRESS,
-
-#define NMP_OBJECT_TYPE_IP_ROUTE(is_ipv4) \
-    ((is_ipv4) ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE)
-    NMP_OBJECT_TYPE_IP4_ROUTE,
-    NMP_OBJECT_TYPE_IP6_ROUTE,
-
-    NMP_OBJECT_TYPE_ROUTING_RULE,
-
-    NMP_OBJECT_TYPE_QDISC,
-
-    NMP_OBJECT_TYPE_TFILTER,
-
-    NMP_OBJECT_TYPE_LNK_BRIDGE,
-    NMP_OBJECT_TYPE_LNK_GRE,
-    NMP_OBJECT_TYPE_LNK_GRETAP,
-    NMP_OBJECT_TYPE_LNK_INFINIBAND,
-    NMP_OBJECT_TYPE_LNK_IP6TNL,
-    NMP_OBJECT_TYPE_LNK_IP6GRE,
-    NMP_OBJECT_TYPE_LNK_IP6GRETAP,
-    NMP_OBJECT_TYPE_LNK_IPIP,
-    NMP_OBJECT_TYPE_LNK_MACSEC,
-    NMP_OBJECT_TYPE_LNK_MACVLAN,
-    NMP_OBJECT_TYPE_LNK_MACVTAP,
-    NMP_OBJECT_TYPE_LNK_SIT,
-    NMP_OBJECT_TYPE_LNK_TUN,
-    NMP_OBJECT_TYPE_LNK_VLAN,
-    NMP_OBJECT_TYPE_LNK_VRF,
-    NMP_OBJECT_TYPE_LNK_VXLAN,
-    NMP_OBJECT_TYPE_LNK_WIREGUARD,
-
-    __NMP_OBJECT_TYPE_LAST,
-    NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1,
-} NMPObjectType;
-
-static inline guint32
-nmp_object_type_to_flags(NMPObjectType obj_type)
-{
-    G_STATIC_ASSERT_EXPR(NMP_OBJECT_TYPE_MAX < 32);
-
-    nm_assert(_NM_INT_NOT_NEGATIVE(obj_type));
-    nm_assert(obj_type < NMP_OBJECT_TYPE_MAX);
-
-    return ((guint32) 1u) << obj_type;
-}
-
 /**
  * NMIPConfigMergeFlags:
  * @NM_IP_CONFIG_MERGE_DEFAULT: no flags set
@@ -236,32 +93,12 @@ typedef enum {
     NM_IP_CONFIG_MERGE_EXTERNAL          = (1LL << 3),
 } NMIPConfigMergeFlags;
 
-/**
- * NMIPRouteTableSyncMode:
- * @NM_IP_ROUTE_TABLE_SYNC_MODE_NONE: indicate an invalid setting.
- * @NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN: only the main table is synced. For all
- *   other tables, NM won't delete any extra routes.
- * @NM_IP_ROUTE_TABLE_SYNC_MODE_FULL: NM will sync all tables, except the
- *   local table (255).
- * @NM_IP_ROUTE_TABLE_SYNC_MODE_ALL: NM will sync all tables, including the
- *   local table (255).
- */
-typedef enum {
-    NM_IP_ROUTE_TABLE_SYNC_MODE_NONE = 0,
-    NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN = 1,
-    NM_IP_ROUTE_TABLE_SYNC_MODE_FULL = 2,
-    NM_IP_ROUTE_TABLE_SYNC_MODE_ALL  = 3,
-} NMIPRouteTableSyncMode;
-
 /* settings */
 typedef struct _NMAgentManager       NMAgentManager;
 typedef struct _NMSecretAgent        NMSecretAgent;
 typedef struct _NMSettings           NMSettings;
 typedef struct _NMSettingsConnection NMSettingsConnection;
 
-/* utils */
-typedef struct _NMUtilsIPv6IfaceId NMUtilsIPv6IfaceId;
-
 #define NM_SETTING_CONNECTION_MDNS_UNKNOWN ((NMSettingConnectionMdns) -42)
 
 #endif /* NM_TYPES_H */
diff --git a/src/core/platform/linux/nl802154.h b/src/core/platform/linux/nl802154.h
deleted file mode 100644
index c21bd079..00000000
--- a/src/core/platform/linux/nl802154.h
+++ /dev/null
@@ -1,450 +0,0 @@
-#ifndef __NL802154_H
-#define __NL802154_H
-/*
- * 802.15.4 netlink interface public header
- *
- * Copyright (C) 2014 Alexander Aring <aar@pengutronix.de>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#define NL802154_GENL_NAME "nl802154"
-
-enum nl802154_commands {
-    /* don't change the order or add anything between, this is ABI! */
-    /* currently we don't shipping this file via uapi, ignore the above one */
-    NL802154_CMD_UNSPEC,
-
-    NL802154_CMD_GET_WPAN_PHY, /* can dump */
-    NL802154_CMD_SET_WPAN_PHY,
-    NL802154_CMD_NEW_WPAN_PHY,
-    NL802154_CMD_DEL_WPAN_PHY,
-
-    NL802154_CMD_GET_INTERFACE, /* can dump */
-    NL802154_CMD_SET_INTERFACE,
-    NL802154_CMD_NEW_INTERFACE,
-    NL802154_CMD_DEL_INTERFACE,
-
-    NL802154_CMD_SET_CHANNEL,
-
-    NL802154_CMD_SET_PAN_ID,
-    NL802154_CMD_SET_SHORT_ADDR,
-
-    NL802154_CMD_SET_TX_POWER,
-    NL802154_CMD_SET_CCA_MODE,
-    NL802154_CMD_SET_CCA_ED_LEVEL,
-
-    NL802154_CMD_SET_MAX_FRAME_RETRIES,
-
-    NL802154_CMD_SET_BACKOFF_EXPONENT,
-    NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
-
-    NL802154_CMD_SET_LBT_MODE,
-
-    NL802154_CMD_SET_ACKREQ_DEFAULT,
-
-    NL802154_CMD_SET_WPAN_PHY_NETNS,
-
-/* add new commands above here */
-
-#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
-    NL802154_CMD_SET_SEC_PARAMS,
-    NL802154_CMD_GET_SEC_KEY, /* can dump */
-    NL802154_CMD_NEW_SEC_KEY,
-    NL802154_CMD_DEL_SEC_KEY,
-    NL802154_CMD_GET_SEC_DEV, /* can dump */
-    NL802154_CMD_NEW_SEC_DEV,
-    NL802154_CMD_DEL_SEC_DEV,
-    NL802154_CMD_GET_SEC_DEVKEY, /* can dump */
-    NL802154_CMD_NEW_SEC_DEVKEY,
-    NL802154_CMD_DEL_SEC_DEVKEY,
-    NL802154_CMD_GET_SEC_LEVEL, /* can dump */
-    NL802154_CMD_NEW_SEC_LEVEL,
-    NL802154_CMD_DEL_SEC_LEVEL,
-#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
-
-    /* used to define NL802154_CMD_MAX below */
-    __NL802154_CMD_AFTER_LAST,
-    NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
-};
-
-enum nl802154_attrs {
-    /* don't change the order or add anything between, this is ABI! */
-    /* currently we don't shipping this file via uapi, ignore the above one */
-    NL802154_ATTR_UNSPEC,
-
-    NL802154_ATTR_WPAN_PHY,
-    NL802154_ATTR_WPAN_PHY_NAME,
-
-    NL802154_ATTR_IFINDEX,
-    NL802154_ATTR_IFNAME,
-    NL802154_ATTR_IFTYPE,
-
-    NL802154_ATTR_WPAN_DEV,
-
-    NL802154_ATTR_PAGE,
-    NL802154_ATTR_CHANNEL,
-
-    NL802154_ATTR_PAN_ID,
-    NL802154_ATTR_SHORT_ADDR,
-
-    NL802154_ATTR_TX_POWER,
-
-    NL802154_ATTR_CCA_MODE,
-    NL802154_ATTR_CCA_OPT,
-    NL802154_ATTR_CCA_ED_LEVEL,
-
-    NL802154_ATTR_MAX_FRAME_RETRIES,
-
-    NL802154_ATTR_MAX_BE,
-    NL802154_ATTR_MIN_BE,
-    NL802154_ATTR_MAX_CSMA_BACKOFFS,
-
-    NL802154_ATTR_LBT_MODE,
-
-    NL802154_ATTR_GENERATION,
-
-    NL802154_ATTR_CHANNELS_SUPPORTED,
-    NL802154_ATTR_SUPPORTED_CHANNEL,
-
-    NL802154_ATTR_EXTENDED_ADDR,
-
-    NL802154_ATTR_WPAN_PHY_CAPS,
-
-    NL802154_ATTR_SUPPORTED_COMMANDS,
-
-    NL802154_ATTR_ACKREQ_DEFAULT,
-
-    NL802154_ATTR_PAD,
-
-    NL802154_ATTR_PID,
-    NL802154_ATTR_NETNS_FD,
-
-/* add attributes here, update the policy in nl802154.c */
-
-#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
-    NL802154_ATTR_SEC_ENABLED,
-    NL802154_ATTR_SEC_OUT_LEVEL,
-    NL802154_ATTR_SEC_OUT_KEY_ID,
-    NL802154_ATTR_SEC_FRAME_COUNTER,
-
-    NL802154_ATTR_SEC_LEVEL,
-    NL802154_ATTR_SEC_DEVICE,
-    NL802154_ATTR_SEC_DEVKEY,
-    NL802154_ATTR_SEC_KEY,
-#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
-
-    __NL802154_ATTR_AFTER_LAST,
-    NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
-};
-
-enum nl802154_iftype {
-    /* for backwards compatibility TODO */
-    NL802154_IFTYPE_UNSPEC = -1,
-
-    NL802154_IFTYPE_NODE,
-    NL802154_IFTYPE_MONITOR,
-    NL802154_IFTYPE_COORD,
-
-    /* keep last */
-    NUM_NL802154_IFTYPES,
-    NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1
-};
-
-/**
- * enum nl802154_wpan_phy_capability_attr - wpan phy capability attributes
- *
- * @__NL802154_CAP_ATTR_INVALID: attribute number 0 is reserved
- * @NL802154_CAP_ATTR_CHANNELS: a nested attribute for nl802154_channel_attr
- * @NL802154_CAP_ATTR_TX_POWERS: a nested attribute for nl802154_wpan_phy_tx_power
- * @NL802154_CAP_ATTR_MIN_CCA_ED_LEVEL: minimum value for cca_ed_level
- * @NL802154_CAP_ATTR_MAX_CCA_ED_LEVEL: maxmimum value for cca_ed_level
- * @NL802154_CAP_ATTR_CCA_MODES: nl802154_cca_modes flags
- * @NL802154_CAP_ATTR_CCA_OPTS: nl802154_cca_opts flags
- * @NL802154_CAP_ATTR_MIN_MINBE: minimum of minbe value
- * @NL802154_CAP_ATTR_MAX_MINBE: maximum of minbe value
- * @NL802154_CAP_ATTR_MIN_MAXBE: minimum of maxbe value
- * @NL802154_CAP_ATTR_MAX_MINBE: maximum of maxbe value
- * @NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS: minimum of csma backoff value
- * @NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS: maximum of csma backoffs value
- * @NL802154_CAP_ATTR_MIN_FRAME_RETRIES: minimum of frame retries value
- * @NL802154_CAP_ATTR_MAX_FRAME_RETRIES: maximum of frame retries value
- * @NL802154_CAP_ATTR_IFTYPES: nl802154_iftype flags
- * @NL802154_CAP_ATTR_LBT: nl802154_supported_bool_states flags
- * @NL802154_CAP_ATTR_MAX: highest cap attribute currently defined
- * @__NL802154_CAP_ATTR_AFTER_LAST: internal use
- */
-enum nl802154_wpan_phy_capability_attr {
-    __NL802154_CAP_ATTR_INVALID,
-
-    NL802154_CAP_ATTR_IFTYPES,
-
-    NL802154_CAP_ATTR_CHANNELS,
-    NL802154_CAP_ATTR_TX_POWERS,
-
-    NL802154_CAP_ATTR_CCA_ED_LEVELS,
-    NL802154_CAP_ATTR_CCA_MODES,
-    NL802154_CAP_ATTR_CCA_OPTS,
-
-    NL802154_CAP_ATTR_MIN_MINBE,
-    NL802154_CAP_ATTR_MAX_MINBE,
-
-    NL802154_CAP_ATTR_MIN_MAXBE,
-    NL802154_CAP_ATTR_MAX_MAXBE,
-
-    NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS,
-    NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS,
-
-    NL802154_CAP_ATTR_MIN_FRAME_RETRIES,
-    NL802154_CAP_ATTR_MAX_FRAME_RETRIES,
-
-    NL802154_CAP_ATTR_LBT,
-
-    /* keep last */
-    __NL802154_CAP_ATTR_AFTER_LAST,
-    NL802154_CAP_ATTR_MAX = __NL802154_CAP_ATTR_AFTER_LAST - 1
-};
-
-/**
- * enum nl802154_cca_modes - cca modes
- *
- * @__NL802154_CCA_INVALID: cca mode number 0 is reserved
- * @NL802154_CCA_ENERGY: Energy above threshold
- * @NL802154_CCA_CARRIER: Carrier sense only
- * @NL802154_CCA_ENERGY_CARRIER: Carrier sense with energy above threshold
- * @NL802154_CCA_ALOHA: CCA shall always report an idle medium
- * @NL802154_CCA_UWB_SHR: UWB preamble sense based on the SHR of a frame
- * @NL802154_CCA_UWB_MULTIPLEXED: UWB preamble sense based on the packet with the multiplexed preamble
- * @__NL802154_CCA_ATTR_AFTER_LAST: Internal
- * @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number
- */
-enum nl802154_cca_modes {
-    __NL802154_CCA_INVALID,
-    NL802154_CCA_ENERGY,
-    NL802154_CCA_CARRIER,
-    NL802154_CCA_ENERGY_CARRIER,
-    NL802154_CCA_ALOHA,
-    NL802154_CCA_UWB_SHR,
-    NL802154_CCA_UWB_MULTIPLEXED,
-
-    /* keep last */
-    __NL802154_CCA_ATTR_AFTER_LAST,
-    NL802154_CCA_ATTR_MAX = __NL802154_CCA_ATTR_AFTER_LAST - 1
-};
-
-/**
- * enum nl802154_cca_opts - additional options for cca modes
- *
- * @NL802154_CCA_OPT_ENERGY_CARRIER_OR: NL802154_CCA_ENERGY_CARRIER with OR
- * @NL802154_CCA_OPT_ENERGY_CARRIER_AND: NL802154_CCA_ENERGY_CARRIER with AND
- */
-enum nl802154_cca_opts {
-    NL802154_CCA_OPT_ENERGY_CARRIER_AND,
-    NL802154_CCA_OPT_ENERGY_CARRIER_OR,
-
-    /* keep last */
-    __NL802154_CCA_OPT_ATTR_AFTER_LAST,
-    NL802154_CCA_OPT_ATTR_MAX = __NL802154_CCA_OPT_ATTR_AFTER_LAST - 1
-};
-
-/**
- * enum nl802154_supported_bool_states - bool states for bool capability entry
- *
- * @NL802154_SUPPORTED_BOOL_FALSE: indicates to set false
- * @NL802154_SUPPORTED_BOOL_TRUE: indicates to set true
- * @__NL802154_SUPPORTED_BOOL_INVALD: reserved
- * @NL802154_SUPPORTED_BOOL_BOTH: indicates to set true and false
- * @__NL802154_SUPPORTED_BOOL_AFTER_LAST: Internal
- * @NL802154_SUPPORTED_BOOL_MAX: highest value for bool states
- */
-enum nl802154_supported_bool_states {
-    NL802154_SUPPORTED_BOOL_FALSE,
-    NL802154_SUPPORTED_BOOL_TRUE,
-    /* to handle them in a mask */
-    __NL802154_SUPPORTED_BOOL_INVALD,
-    NL802154_SUPPORTED_BOOL_BOTH,
-
-    /* keep last */
-    __NL802154_SUPPORTED_BOOL_AFTER_LAST,
-    NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1
-};
-
-#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
-
-enum nl802154_dev_addr_modes {
-    NL802154_DEV_ADDR_NONE,
-    __NL802154_DEV_ADDR_INVALID,
-    NL802154_DEV_ADDR_SHORT,
-    NL802154_DEV_ADDR_EXTENDED,
-
-    /* keep last */
-    __NL802154_DEV_ADDR_AFTER_LAST,
-    NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1
-};
-
-enum nl802154_dev_addr_attrs {
-    NL802154_DEV_ADDR_ATTR_UNSPEC,
-
-    NL802154_DEV_ADDR_ATTR_PAN_ID,
-    NL802154_DEV_ADDR_ATTR_MODE,
-    NL802154_DEV_ADDR_ATTR_SHORT,
-    NL802154_DEV_ADDR_ATTR_EXTENDED,
-    NL802154_DEV_ADDR_ATTR_PAD,
-
-    /* keep last */
-    __NL802154_DEV_ADDR_ATTR_AFTER_LAST,
-    NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1
-};
-
-enum nl802154_key_id_modes {
-    NL802154_KEY_ID_MODE_IMPLICIT,
-    NL802154_KEY_ID_MODE_INDEX,
-    NL802154_KEY_ID_MODE_INDEX_SHORT,
-    NL802154_KEY_ID_MODE_INDEX_EXTENDED,
-
-    /* keep last */
-    __NL802154_KEY_ID_MODE_AFTER_LAST,
-    NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1
-};
-
-enum nl802154_key_id_attrs {
-    NL802154_KEY_ID_ATTR_UNSPEC,
-
-    NL802154_KEY_ID_ATTR_MODE,
-    NL802154_KEY_ID_ATTR_INDEX,
-    NL802154_KEY_ID_ATTR_IMPLICIT,
-    NL802154_KEY_ID_ATTR_SOURCE_SHORT,
-    NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
-    NL802154_KEY_ID_ATTR_PAD,
-
-    /* keep last */
-    __NL802154_KEY_ID_ATTR_AFTER_LAST,
-    NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1
-};
-
-enum nl802154_seclevels {
-    NL802154_SECLEVEL_NONE,
-    NL802154_SECLEVEL_MIC32,
-    NL802154_SECLEVEL_MIC64,
-    NL802154_SECLEVEL_MIC128,
-    NL802154_SECLEVEL_ENC,
-    NL802154_SECLEVEL_ENC_MIC32,
-    NL802154_SECLEVEL_ENC_MIC64,
-    NL802154_SECLEVEL_ENC_MIC128,
-
-    /* keep last */
-    __NL802154_SECLEVEL_AFTER_LAST,
-    NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1
-};
-
-enum nl802154_frames {
-    NL802154_FRAME_BEACON,
-    NL802154_FRAME_DATA,
-    NL802154_FRAME_ACK,
-    NL802154_FRAME_CMD,
-
-    /* keep last */
-    __NL802154_FRAME_AFTER_LAST,
-    NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1
-};
-
-enum nl802154_cmd_frames {
-    __NL802154_CMD_FRAME_INVALID,
-    NL802154_CMD_FRAME_ASSOC_REQUEST,
-    NL802154_CMD_FRAME_ASSOC_RESPONSE,
-    NL802154_CMD_FRAME_DISASSOC_NOTIFY,
-    NL802154_CMD_FRAME_DATA_REQUEST,
-    NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY,
-    NL802154_CMD_FRAME_ORPHAN_NOTIFY,
-    NL802154_CMD_FRAME_BEACON_REQUEST,
-    NL802154_CMD_FRAME_COORD_REALIGNMENT,
-    NL802154_CMD_FRAME_GTS_REQUEST,
-
-    /* keep last */
-    __NL802154_CMD_FRAME_AFTER_LAST,
-    NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1
-};
-
-enum nl802154_seclevel_attrs {
-    NL802154_SECLEVEL_ATTR_UNSPEC,
-
-    NL802154_SECLEVEL_ATTR_LEVELS,
-    NL802154_SECLEVEL_ATTR_FRAME,
-    NL802154_SECLEVEL_ATTR_CMD_FRAME,
-    NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
-
-    /* keep last */
-    __NL802154_SECLEVEL_ATTR_AFTER_LAST,
-    NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1
-};
-
-/* TODO what is this? couldn't find in mib */
-enum {
-    NL802154_DEVKEY_IGNORE,
-    NL802154_DEVKEY_RESTRICT,
-    NL802154_DEVKEY_RECORD,
-
-    /* keep last */
-    __NL802154_DEVKEY_AFTER_LAST,
-    NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1
-};
-
-enum nl802154_dev {
-    NL802154_DEV_ATTR_UNSPEC,
-
-    NL802154_DEV_ATTR_FRAME_COUNTER,
-    NL802154_DEV_ATTR_PAN_ID,
-    NL802154_DEV_ATTR_SHORT_ADDR,
-    NL802154_DEV_ATTR_EXTENDED_ADDR,
-    NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
-    NL802154_DEV_ATTR_KEY_MODE,
-    NL802154_DEV_ATTR_PAD,
-
-    /* keep last */
-    __NL802154_DEV_ATTR_AFTER_LAST,
-    NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1
-};
-
-enum nl802154_devkey {
-    NL802154_DEVKEY_ATTR_UNSPEC,
-
-    NL802154_DEVKEY_ATTR_FRAME_COUNTER,
-    NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
-    NL802154_DEVKEY_ATTR_ID,
-    NL802154_DEVKEY_ATTR_PAD,
-
-    /* keep last */
-    __NL802154_DEVKEY_ATTR_AFTER_LAST,
-    NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1
-};
-
-enum nl802154_key {
-    NL802154_KEY_ATTR_UNSPEC,
-
-    NL802154_KEY_ATTR_ID,
-    NL802154_KEY_ATTR_USAGE_FRAMES,
-    NL802154_KEY_ATTR_USAGE_CMDS,
-    NL802154_KEY_ATTR_BYTES,
-
-    /* keep last */
-    __NL802154_KEY_ATTR_AFTER_LAST,
-    NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1
-};
-
-    #define NL802154_KEY_SIZE         16
-    #define NL802154_CMD_FRAME_NR_IDS 256
-
-#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
-
-#endif /* __NL802154_H */
diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c
index 81c9f06c..0108b581 100644
--- a/src/core/platform/nm-fake-platform.c
+++ b/src/core/platform/nm-fake-platform.c
@@ -13,12 +13,9 @@
 #include <linux/if.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-utils.h"
-
-#include "nm-core-utils.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "nm-platform-private.h"
-#include "nmp-object.h"
+#include "libnm-platform/nm-platform-utils.h"
+#include "libnm-platform/nm-platform-private.h"
+#include "libnm-platform/nmp-object.h"
 
 #include "nm-test-utils-core.h"
 
@@ -506,6 +503,21 @@ link_set_flags(NMPlatform *platform, NMFakePlatformLink *device, guint n_ifi_fla
     link_set_obj(platform, device, obj_tmp);
 }
 
+static int
+link_change_flags(NMPlatform *platform, int ifindex, unsigned flags_mask, unsigned flags_set)
+{
+    NMFakePlatformLink *device = link_get(platform, ifindex);
+
+    if (!device)
+        return -ENOENT;
+
+    link_set_flags(platform,
+                   device,
+                   NM_FLAGS_ASSIGN_MASK(device->obj->link.n_ifi_flags, flags_mask, flags_set));
+
+    return 0;
+}
+
 static void
 link_changed(NMPlatform *        platform,
              NMFakePlatformLink *device,
@@ -543,72 +555,13 @@ link_changed(NMPlatform *        platform,
     }
 }
 
-static gboolean
-link_set_up(NMPlatform *platform, int ifindex, gboolean *out_no_firmware)
-{
-    NMFakePlatformLink *device = link_get(platform, ifindex);
-
-    if (out_no_firmware)
-        *out_no_firmware = FALSE;
-
-    if (!device) {
-        _LOGE("failure changing link: netlink error (No such device)");
-        return FALSE;
-    }
-
-    link_set_flags(platform, device, NM_FLAGS_ASSIGN(device->obj->link.n_ifi_flags, IFF_UP, TRUE));
-    return TRUE;
-}
-
-static gboolean
-link_set_down(NMPlatform *platform, int ifindex)
-{
-    NMFakePlatformLink *device = link_get(platform, ifindex);
-
-    if (!device) {
-        _LOGE("failure changing link: netlink error (No such device)");
-        return FALSE;
-    }
-
-    link_set_flags(platform, device, NM_FLAGS_UNSET(device->obj->link.n_ifi_flags, IFF_UP));
-    return TRUE;
-}
-
-static gboolean
-link_set_arp(NMPlatform *platform, int ifindex)
-{
-    NMFakePlatformLink *device = link_get(platform, ifindex);
-
-    if (!device) {
-        _LOGE("failure changing link: netlink error (No such device)");
-        return FALSE;
-    }
-
-    link_set_flags(platform, device, NM_FLAGS_UNSET(device->obj->link.n_ifi_flags, IFF_NOARP));
-    return TRUE;
-}
-
-static gboolean
-link_set_noarp(NMPlatform *platform, int ifindex)
-{
-    NMFakePlatformLink *device = link_get(platform, ifindex);
-
-    if (!device) {
-        _LOGE("failure changing link: netlink error (No such device)");
-        return FALSE;
-    }
-
-    link_set_flags(platform, device, NM_FLAGS_SET(device->obj->link.n_ifi_flags, IFF_NOARP));
-    return TRUE;
-}
-
 static int
 link_set_address(NMPlatform *platform, int ifindex, gconstpointer addr, size_t len)
 {
     NMFakePlatformLink *device        = link_get(platform, ifindex);
     nm_auto_nmpobj NMPObject *obj_tmp = NULL;
 
-    if (len == 0 || len > NM_UTILS_HWADDR_LEN_MAX || !addr)
+    if (len == 0 || len > _NM_UTILS_HWADDR_LEN_MAX || !addr)
         g_return_val_if_reached(-NME_BUG);
 
     if (!device)
@@ -749,8 +702,8 @@ link_release(NMPlatform *platform, int master_idx, int slave_idx)
 static gboolean
 link_vlan_change(NMPlatform *            platform,
                  int                     ifindex,
-                 NMVlanFlags             flags_mask,
-                 NMVlanFlags             flags_set,
+                 _NMVlanFlags            flags_mask,
+                 _NMVlanFlags            flags_set,
                  gboolean                ingress_reset_all,
                  const NMVlanQosMapping *ingress_map,
                  gsize                   n_ingress_map,
@@ -799,7 +752,7 @@ infiniband_partition_add(NMPlatform *           platform,
     parent_device = link_get(platform, parent);
     g_return_val_if_fail(parent_device != NULL, FALSE);
 
-    nm_utils_new_infiniband_name(name, parent_device->obj->link.name, p_key);
+    nmp_utils_new_infiniband_name(name, parent_device->obj->link.name, p_key);
 
     link_add_one(platform, name, NM_LINK_TYPE_INFINIBAND, _infiniband_add_prepare, &d, out_link);
     return TRUE;
@@ -814,12 +767,12 @@ infiniband_partition_delete(NMPlatform *platform, int parent, int p_key)
     parent_device = link_get(platform, parent);
     g_return_val_if_fail(parent_device != NULL, FALSE);
 
-    nm_utils_new_infiniband_name(name, parent_device->obj->link.name, p_key);
+    nmp_utils_new_infiniband_name(name, parent_device->obj->link.name, p_key);
     return link_delete(platform, nm_platform_link_get_ifindex(platform, name));
 }
 
 static gboolean
-wifi_get_capabilities(NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
+wifi_get_capabilities(NMPlatform *platform, int ifindex, _NMDeviceWifiCapabilities *caps)
 {
     NMFakePlatformLink *device = link_get(platform, ifindex);
 
@@ -829,10 +782,10 @@ wifi_get_capabilities(NMPlatform *platform, int ifindex, NMDeviceWifiCapabilitie
         return FALSE;
 
     if (caps) {
-        *caps = (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104
-                 | NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP
-                 | NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN | NM_WIFI_DEVICE_CAP_AP
-                 | NM_WIFI_DEVICE_CAP_ADHOC);
+        *caps = (_NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | _NM_WIFI_DEVICE_CAP_CIPHER_WEP104
+                 | _NM_WIFI_DEVICE_CAP_CIPHER_TKIP | _NM_WIFI_DEVICE_CAP_CIPHER_CCMP
+                 | _NM_WIFI_DEVICE_CAP_WPA | _NM_WIFI_DEVICE_CAP_RSN | _NM_WIFI_DEVICE_CAP_AP
+                 | _NM_WIFI_DEVICE_CAP_ADHOC);
     }
     return TRUE;
 }
@@ -861,14 +814,14 @@ wifi_get_rate(NMPlatform *platform, int ifindex)
     return 0;
 }
 
-static NM80211Mode
+static _NM80211Mode
 wifi_get_mode(NMPlatform *platform, int ifindex)
 {
-    return NM_802_11_MODE_UNKNOWN;
+    return _NM_802_11_MODE_UNKNOWN;
 }
 
 static void
-wifi_set_mode(NMPlatform *platform, int ifindex, NM80211Mode mode)
+wifi_set_mode(NMPlatform *platform, int ifindex, _NM80211Mode mode)
 {
     ;
 }
@@ -1357,14 +1310,11 @@ nm_fake_platform_class_init(NMFakePlatformClass *klass)
     platform_class->link_add    = link_add;
     platform_class->link_delete = link_delete;
 
-    platform_class->link_set_up    = link_set_up;
-    platform_class->link_set_down  = link_set_down;
-    platform_class->link_set_arp   = link_set_arp;
-    platform_class->link_set_noarp = link_set_noarp;
-
     platform_class->link_set_address = link_set_address;
     platform_class->link_set_mtu     = link_set_mtu;
 
+    platform_class->link_change_flags = link_change_flags;
+
     platform_class->link_get_driver_info = link_get_driver_info;
 
     platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
diff --git a/src/core/platform/nm-fake-platform.h b/src/core/platform/nm-fake-platform.h
index a1d44f8f..8f8571fa 100644
--- a/src/core/platform/nm-fake-platform.h
+++ b/src/core/platform/nm-fake-platform.h
@@ -6,7 +6,7 @@
 #ifndef __NETWORKMANAGER_FAKE_PLATFORM_H__
 #define __NETWORKMANAGER_FAKE_PLATFORM_H__
 
-#include "nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #define NM_TYPE_FAKE_PLATFORM (nm_fake_platform_get_type())
 #define NM_FAKE_PLATFORM(obj) \
diff --git a/src/core/platform/nm-linux-platform.c b/src/core/platform/nm-linux-platform.c
deleted file mode 100644
index 7bd42bba..00000000
--- a/src/core/platform/nm-linux-platform.c
+++ /dev/null
@@ -1,9696 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2012 - 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-linux-platform.h"
-
-#include <arpa/inet.h>
-#include <dlfcn.h>
-#include <endian.h>
-#include <fcntl.h>
-#include <libudev.h>
-#include <net/ethernet.h>
-#include <linux/fib_rules.h>
-#include <linux/ip.h>
-#include <linux/if.h>
-#include <linux/if_bridge.h>
-#include <linux/if_link.h>
-#include <linux/if_tun.h>
-#include <linux/if_tunnel.h>
-#include <linux/if_vlan.h>
-#include <linux/ip6_tunnel.h>
-#include <linux/tc_act/tc_mirred.h>
-#include <netinet/icmp6.h>
-#include <netinet/in.h>
-#include <net/if_arp.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/statvfs.h>
-#include <unistd.h>
-
-#include "nm-std-aux/unaligned.h"
-
-#include "nm-utils.h"
-#include "nm-core-internal.h"
-#include "nm-setting-vlan.h"
-
-#include "nm-glib-aux/nm-secret-utils.h"
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-platform/nm-netlink.h"
-#include "nm-core-utils.h"
-#include "nmp-object.h"
-#include "nm-platform/nmp-netns.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "nm-platform-private.h"
-#include "wifi/nm-wifi-utils.h"
-#include "wifi/nm-wifi-utils-wext.h"
-#include "wpan/nm-wpan-utils.h"
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-udev-aux/nm-udev-utils.h"
-
-/*****************************************************************************/
-
-/* re-implement <linux/tc_act/tc_defact.h> to build against kernel
- * headers that lack this. */
-
-#include <linux/pkt_cls.h>
-
-struct tc_defact {
-    tc_gen;
-};
-
-enum { TCA_DEF_UNSPEC, TCA_DEF_TM, TCA_DEF_PARMS, TCA_DEF_DATA, TCA_DEF_PAD, __TCA_DEF_MAX };
-#define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
-
-/*****************************************************************************/
-
-/* Compat with older kernels. */
-
-#define TCA_FQ_CODEL_CE_THRESHOLD 7
-#define TCA_FQ_CODEL_MEMORY_LIMIT 9
-
-/*****************************************************************************/
-
-#define VLAN_FLAG_MVRP 0x8
-
-/*****************************************************************************/
-
-#define IFQDISCSIZ 32
-
-/*****************************************************************************/
-
-#ifndef IFLA_PROMISCUITY
-    #define IFLA_PROMISCUITY 30
-#endif
-#define IFLA_NUM_TX_QUEUES 31
-#define IFLA_NUM_RX_QUEUES 32
-#define IFLA_CARRIER       33
-#define IFLA_PHYS_PORT_ID  34
-#define IFLA_LINK_NETNSID  37
-#define __IFLA_MAX         39
-
-#define IFLA_INET6_TOKEN         7
-#define IFLA_INET6_ADDR_GEN_MODE 8
-#define __IFLA_INET6_MAX         9
-
-#define IFLA_VLAN_PROTOCOL 5
-#define __IFLA_VLAN_MAX    6
-
-#define IFA_FLAGS 8
-#define __IFA_MAX 9
-
-#define IFLA_MACVLAN_FLAGS 2
-#define __IFLA_MACVLAN_MAX 3
-
-#define IFLA_IPTUN_LINK        1
-#define IFLA_IPTUN_LOCAL       2
-#define IFLA_IPTUN_REMOTE      3
-#define IFLA_IPTUN_TTL         4
-#define IFLA_IPTUN_TOS         5
-#define IFLA_IPTUN_ENCAP_LIMIT 6
-#define IFLA_IPTUN_FLOWINFO    7
-#define IFLA_IPTUN_FLAGS       8
-#define IFLA_IPTUN_PROTO       9
-#define IFLA_IPTUN_PMTUDISC    10
-#define __IFLA_IPTUN_MAX       19
-#ifndef IFLA_IPTUN_MAX
-    #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
-#endif
-
-#define IFLA_TUN_UNSPEC              0
-#define IFLA_TUN_OWNER               1
-#define IFLA_TUN_GROUP               2
-#define IFLA_TUN_TYPE                3
-#define IFLA_TUN_PI                  4
-#define IFLA_TUN_VNET_HDR            5
-#define IFLA_TUN_PERSIST             6
-#define IFLA_TUN_MULTI_QUEUE         7
-#define IFLA_TUN_NUM_QUEUES          8
-#define IFLA_TUN_NUM_DISABLED_QUEUES 9
-#define __IFLA_TUN_MAX               10
-#define IFLA_TUN_MAX                 (__IFLA_TUN_MAX - 1)
-
-G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1));
-#define RTA_PREF 20
-#undef RTA_MAX
-#define RTA_MAX (MAX((__RTA_MAX - 1), RTA_PREF))
-
-#ifndef MACVLAN_FLAG_NOPROMISC
-    #define MACVLAN_FLAG_NOPROMISC 1
-#endif
-
-#define IP6_FLOWINFO_TCLASS_MASK    0x0FF00000
-#define IP6_FLOWINFO_TCLASS_SHIFT   20
-#define IP6_FLOWINFO_FLOWLABEL_MASK 0x000FFFFF
-
-#define IFLA_BR_VLAN_STATS_ENABLED 41
-
-/*****************************************************************************/
-
-/* Appeared in the kernel prior to 3.13 dated 19 January, 2014 */
-#ifndef ARPHRD_6LOWPAN
-    #define ARPHRD_6LOWPAN 825
-#endif
-
-/*****************************************************************************/
-
-#define FRA_TUN_ID             12
-#define FRA_SUPPRESS_IFGROUP   13
-#define FRA_SUPPRESS_PREFIXLEN 14
-#define FRA_PAD                18
-#define FRA_L3MDEV             19
-#define FRA_UID_RANGE          20
-#define FRA_PROTOCOL           21
-#define FRA_IP_PROTO           22
-#define FRA_SPORT_RANGE        23
-#define FRA_DPORT_RANGE        24
-
-/*****************************************************************************/
-
-#define IFLA_MACSEC_UNSPEC         0
-#define IFLA_MACSEC_SCI            1
-#define IFLA_MACSEC_PORT           2
-#define IFLA_MACSEC_ICV_LEN        3
-#define IFLA_MACSEC_CIPHER_SUITE   4
-#define IFLA_MACSEC_WINDOW         5
-#define IFLA_MACSEC_ENCODING_SA    6
-#define IFLA_MACSEC_ENCRYPT        7
-#define IFLA_MACSEC_PROTECT        8
-#define IFLA_MACSEC_INC_SCI        9
-#define IFLA_MACSEC_ES             10
-#define IFLA_MACSEC_SCB            11
-#define IFLA_MACSEC_REPLAY_PROTECT 12
-#define IFLA_MACSEC_VALIDATION     13
-#define IFLA_MACSEC_PAD            14
-#define __IFLA_MACSEC_MAX          15
-
-/*****************************************************************************/
-
-#define WG_CMD_GET_DEVICE 0
-#define WG_CMD_SET_DEVICE 1
-
-#define WGDEVICE_F_REPLACE_PEERS ((guint32)(1U << 0))
-
-#define WGPEER_F_REMOVE_ME          ((guint32)(1U << 0))
-#define WGPEER_F_REPLACE_ALLOWEDIPS ((guint32)(1U << 1))
-
-#define WGDEVICE_A_UNSPEC      0
-#define WGDEVICE_A_IFINDEX     1
-#define WGDEVICE_A_IFNAME      2
-#define WGDEVICE_A_PRIVATE_KEY 3
-#define WGDEVICE_A_PUBLIC_KEY  4
-#define WGDEVICE_A_FLAGS       5
-#define WGDEVICE_A_LISTEN_PORT 6
-#define WGDEVICE_A_FWMARK      7
-#define WGDEVICE_A_PEERS       8
-#define WGDEVICE_A_MAX         8
-
-#define WGPEER_A_UNSPEC                        0
-#define WGPEER_A_PUBLIC_KEY                    1
-#define WGPEER_A_PRESHARED_KEY                 2
-#define WGPEER_A_FLAGS                         3
-#define WGPEER_A_ENDPOINT                      4
-#define WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL 5
-#define WGPEER_A_LAST_HANDSHAKE_TIME           6
-#define WGPEER_A_RX_BYTES                      7
-#define WGPEER_A_TX_BYTES                      8
-#define WGPEER_A_ALLOWEDIPS                    9
-#define WGPEER_A_MAX                           9
-
-#define WGALLOWEDIP_A_UNSPEC    0
-#define WGALLOWEDIP_A_FAMILY    1
-#define WGALLOWEDIP_A_IPADDR    2
-#define WGALLOWEDIP_A_CIDR_MASK 3
-#define WGALLOWEDIP_A_MAX       3
-
-/*****************************************************************************/
-
-/* Redefine VF enums and structures that are not available on older kernels. */
-
-#define IFLA_VF_UNSPEC       0
-#define IFLA_VF_MAC          1
-#define IFLA_VF_VLAN         2
-#define IFLA_VF_TX_RATE      3
-#define IFLA_VF_SPOOFCHK     4
-#define IFLA_VF_LINK_STATE   5
-#define IFLA_VF_RATE         6
-#define IFLA_VF_RSS_QUERY_EN 7
-#define IFLA_VF_STATS        8
-#define IFLA_VF_TRUST        9
-#define IFLA_VF_IB_NODE_GUID 10
-#define IFLA_VF_IB_PORT_GUID 11
-#define IFLA_VF_VLAN_LIST    12
-
-#define IFLA_VF_VLAN_INFO_UNSPEC 0
-#define IFLA_VF_VLAN_INFO        1
-
-/* valid for TRUST, SPOOFCHK, LINK_STATE, RSS_QUERY_EN */
-struct _ifla_vf_setting {
-    guint32 vf;
-    guint32 setting;
-};
-
-struct _ifla_vf_rate {
-    guint32 vf;
-    guint32 min_tx_rate;
-    guint32 max_tx_rate;
-};
-
-struct _ifla_vf_vlan_info {
-    guint32 vf;
-    guint32 vlan; /* 0 - 4095, 0 disables VLAN filter */
-    guint32 qos;
-    guint16 vlan_proto; /* VLAN protocol, either 802.1Q or 802.1ad */
-};
-
-/*****************************************************************************/
-
-/* Appeared in the kernel 4.0 dated April 12, 2015 */
-#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN
-    #define BRIDGE_VLAN_INFO_RANGE_BEGIN (1 << 3) /* VLAN is start of vlan range */
-    #define BRIDGE_VLAN_INFO_RANGE_END   (1 << 4) /* VLAN is end of vlan range */
-#endif
-
-/*****************************************************************************/
-
-#define PSCHED_TIME_UNITS_PER_SEC 1000000
-
-/*****************************************************************************/
-
-typedef enum {
-    INFINIBAND_ACTION_CREATE_CHILD,
-    INFINIBAND_ACTION_DELETE_CHILD,
-} InfinibandAction;
-
-typedef enum {
-    CHANGE_LINK_TYPE_UNSPEC,
-    CHANGE_LINK_TYPE_SET_MTU,
-    CHANGE_LINK_TYPE_SET_ADDRESS,
-} ChangeLinkType;
-
-typedef struct {
-    union {
-        struct {
-            gconstpointer address;
-            gsize         length;
-        } set_address;
-    };
-} ChangeLinkData;
-
-typedef enum {
-    _REFRESH_ALL_TYPE_FIRST = 0,
-
-    REFRESH_ALL_TYPE_LINKS             = 0,
-    REFRESH_ALL_TYPE_IP4_ADDRESSES     = 1,
-    REFRESH_ALL_TYPE_IP6_ADDRESSES     = 2,
-    REFRESH_ALL_TYPE_IP4_ROUTES        = 3,
-    REFRESH_ALL_TYPE_IP6_ROUTES        = 4,
-    REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5,
-    REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6,
-    REFRESH_ALL_TYPE_QDISCS            = 7,
-    REFRESH_ALL_TYPE_TFILTERS          = 8,
-
-    _REFRESH_ALL_TYPE_NUM,
-} RefreshAllType;
-
-typedef struct {
-    NMPObjectType obj_type;
-
-    /* for NLM_F_DUMP, which address family to request. */
-    int addr_family;
-} RefreshAllInfo;
-
-typedef enum {
-    DELAYED_ACTION_TYPE_NONE = 0,
-
-#define F(val, name) ((sizeof(char[(((val)) == (name)) ? 1 : -1]) * 0) + (val))
-    DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS             = 1 << F(0, REFRESH_ALL_TYPE_LINKS),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES     = 1 << F(1, REFRESH_ALL_TYPE_IP4_ADDRESSES),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES     = 1 << F(2, REFRESH_ALL_TYPE_IP6_ADDRESSES),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES        = 1 << F(3, REFRESH_ALL_TYPE_IP4_ROUTES),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES        = 1 << F(4, REFRESH_ALL_TYPE_IP6_ROUTES),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1
-                                                        << F(5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1
-                                                        << F(6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS   = 1 << F(7, REFRESH_ALL_TYPE_QDISCS),
-    DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F(8, REFRESH_ALL_TYPE_TFILTERS),
-#undef F
-
-    DELAYED_ACTION_TYPE_REFRESH_LINK         = 1 << 9,
-    DELAYED_ACTION_TYPE_MASTER_CONNECTED     = 1 << 10,
-    DELAYED_ACTION_TYPE_READ_NETLINK         = 1 << 11,
-    DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12,
-
-    __DELAYED_ACTION_TYPE_MAX,
-
-    DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL =
-        DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4
-        | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6,
-
-    DELAYED_ACTION_TYPE_REFRESH_ALL =
-        DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES
-        | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
-        | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES
-        | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS
-        | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
-
-    DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX - 1,
-} DelayedActionType;
-
-#define FOR_EACH_DELAYED_ACTION(iflags, flags_all)          \
-    for ((iflags) = (DelayedActionType) 0x1LL; ({           \
-             gboolean _good = FALSE;                        \
-                                                            \
-             nm_assert(nm_utils_is_power_of_two(iflags));   \
-                                                            \
-             while ((iflags) <= DELAYED_ACTION_TYPE_MAX) {  \
-                 if (NM_FLAGS_ANY((flags_all), (iflags))) { \
-                     _good = TRUE;                          \
-                     break;                                 \
-                 }                                          \
-                 (iflags) <<= 1;                            \
-             }                                              \
-             _good;                                         \
-         });                                                \
-         (iflags) <<= 1)
-
-typedef enum {
-    /* Negative values are errors from kernel. Add dummy member to
-     * make enum signed. */
-    _WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = G_MININT,
-
-    WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN = 0,
-    WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK,
-    WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN,
-    WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC,
-    WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL,
-    WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT,
-    WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING,
-    WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS,
-} WaitForNlResponseResult;
-
-typedef enum {
-    DELAYED_ACTION_RESPONSE_TYPE_VOID                    = 0,
-    DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1,
-    DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET               = 2,
-} DelayedActionWaitForNlResponseType;
-
-typedef struct {
-    guint32                            seq_number;
-    WaitForNlResponseResult            seq_result;
-    DelayedActionWaitForNlResponseType response_type;
-    gint64                             timeout_abs_ns;
-    WaitForNlResponseResult *          out_seq_result;
-    char **                            out_errmsg;
-    union {
-        int *       out_refresh_all_in_progress;
-        NMPObject **out_route_get;
-        gpointer    out_data;
-    } response;
-} DelayedActionWaitForNlResponseData;
-
-/*****************************************************************************/
-
-typedef struct {
-    struct nl_sock *genl;
-
-    struct nl_sock *nlh;
-
-    GSource *event_source;
-
-    guint32 nlh_seq_next;
-#if NM_MORE_LOGGING
-    guint32 nlh_seq_last_handled;
-#endif
-    guint32 nlh_seq_last_seen;
-
-    guint32 pruning[_REFRESH_ALL_TYPE_NUM];
-
-    GHashTable *sysctl_get_prev_values;
-    CList       sysctl_list;
-
-    NMUdevClient *udev_client;
-
-    struct {
-        /* which delayed actions are scheduled, as marked in @flags.
-         * Some types have additional arguments in the fields below. */
-        DelayedActionType flags;
-
-        /* counter that a refresh all action is in progress, separated
-         * by type. */
-        int refresh_all_in_progress[_REFRESH_ALL_TYPE_NUM];
-
-        GPtrArray *list_master_connected;
-        GPtrArray *list_refresh_link;
-        GArray *   list_wait_for_nl_response;
-
-        int is_handling;
-    } delayed_action;
-} NMLinuxPlatformPrivate;
-
-struct _NMLinuxPlatform {
-    NMPlatform             parent;
-    NMLinuxPlatformPrivate _priv;
-};
-
-struct _NMLinuxPlatformClass {
-    NMPlatformClass parent;
-};
-
-G_DEFINE_TYPE(NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
-
-#define NM_LINUX_PLATFORM_GET_PRIVATE(self) \
-    _NM_GET_PRIVATE(self, NMLinuxPlatform, NM_IS_LINUX_PLATFORM, NMPlatform)
-
-/*****************************************************************************/
-
-#define _NMLOG_PREFIX_NAME             "platform-linux"
-#define _NMLOG_DOMAIN                  LOGD_PLATFORM
-#define _NMLOG2_DOMAIN                 LOGD_PLATFORM
-#define _NMLOG(level, ...)             _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__)
-#define _NMLOG_err(errsv, level, ...)  _LOG_err(errsv, level, _NMLOG_DOMAIN, platform, __VA_ARGS__)
-#define _NMLOG2(level, ...)            _LOG(level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__)
-#define _NMLOG2_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__)
-
-#define _LOG_print(__level, __domain, __errsv, self, ...)                                 \
-    G_STMT_START                                                                          \
-    {                                                                                     \
-        char              __prefix[32];                                                   \
-        const char *      __p_prefix = _NMLOG_PREFIX_NAME;                                \
-        NMPlatform *const __self     = (self);                                            \
-                                                                                          \
-        if (__self && nm_platform_get_log_with_ptr(__self)) {                             \
-            g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
-            __p_prefix = __prefix;                                                        \
-        }                                                                                 \
-        _nm_log(__level,                                                                  \
-                __domain,                                                                 \
-                __errsv,                                                                  \
-                NULL,                                                                     \
-                NULL,                                                                     \
-                "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                                \
-                __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__));                            \
-    }                                                                                     \
-    G_STMT_END
-
-#define _LOG(level, domain, self, ...)                           \
-    G_STMT_START                                                 \
-    {                                                            \
-        const NMLogLevel  __level  = (level);                    \
-        const NMLogDomain __domain = (domain);                   \
-                                                                 \
-        if (nm_logging_enabled(__level, __domain)) {             \
-            _LOG_print(__level, __domain, 0, self, __VA_ARGS__); \
-        }                                                        \
-    }                                                            \
-    G_STMT_END
-
-#define _LOG_err(errsv, level, domain, self, ...)                                                   \
-    G_STMT_START                                                                                    \
-    {                                                                                               \
-        const NMLogLevel  __level  = (level);                                                       \
-        const NMLogDomain __domain = (domain);                                                      \
-                                                                                                    \
-        if (nm_logging_enabled(__level, __domain)) {                                                \
-            int __errsv = (errsv);                                                                  \
-                                                                                                    \
-            /* The %m format specifier (GNU extension) would already allow you to specify the error
-             * message conveniently (and nm_log would get that right too). But we don't want to depend
-             * on that, so instead append the message at the end.
-             * Currently, users are expected not to use %m in the format string. */ \
-            _LOG_print(                                                                             \
-                __level,                                                                            \
-                __domain,                                                                           \
-                __errsv,                                                                            \
-                self,                                                                               \
-                _NM_UTILS_MACRO_FIRST(__VA_ARGS__) ": %s (%d)" _NM_UTILS_MACRO_REST(__VA_ARGS__),   \
-                nm_strerror_native(__errsv),                                                        \
-                __errsv);                                                                           \
-        }                                                                                           \
-    }                                                                                               \
-    G_STMT_END
-
-/*****************************************************************************/
-
-static void
-delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpointer user_data);
-static gboolean delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink);
-static void do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name);
-static void do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type);
-static void cache_on_change(NMPlatform *     platform,
-                            NMPCacheOpsType  cache_op,
-                            const NMPObject *obj_old,
-                            const NMPObject *obj_new);
-static void cache_prune_all(NMPlatform *platform);
-static gboolean        event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks);
-static struct nl_sock *_genl_sock(NMLinuxPlatform *platform);
-
-/*****************************************************************************/
-
-static int
-wait_for_nl_response_to_nmerr(WaitForNlResponseResult seq_result)
-{
-    if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
-        return 0;
-    if (seq_result < 0)
-        return (int) seq_result;
-    return -NME_PL_NETLINK;
-}
-
-static const char *
-wait_for_nl_response_to_string(WaitForNlResponseResult seq_result,
-                               const char *            errmsg,
-                               char *                  buf,
-                               gsize                   buf_size)
-{
-    char *buf0 = buf;
-
-    switch (seq_result) {
-    case WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN:
-        nm_utils_strbuf_append_str(&buf, &buf_size, "unknown");
-        break;
-    case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK:
-        nm_utils_strbuf_append_str(&buf, &buf_size, "success");
-        break;
-    case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN:
-        nm_utils_strbuf_append_str(&buf, &buf_size, "failure");
-        break;
-    default:
-        if (seq_result < 0) {
-            nm_utils_strbuf_append(&buf,
-                                   &buf_size,
-                                   "failure %d (%s%s%s)",
-                                   -((int) seq_result),
-                                   nm_strerror_native(-((int) seq_result)),
-                                   errmsg ? " - " : "",
-                                   errmsg ?: "");
-        } else
-            nm_utils_strbuf_append(&buf, &buf_size, "internal failure %d", (int) seq_result);
-        break;
-    }
-    return buf0;
-}
-
-/******************************************************************
- * Various utilities
- ******************************************************************/
-
-static int
-_vlan_qos_mapping_cmp_from(gconstpointer a, gconstpointer b, gpointer user_data)
-{
-    const NMVlanQosMapping *map_a = a;
-    const NMVlanQosMapping *map_b = b;
-
-    if (map_a->from != map_b->from)
-        return map_a->from < map_b->from ? -1 : 1;
-    return 0;
-}
-
-static int
-_vlan_qos_mapping_cmp_from_ptr(gconstpointer a, gconstpointer b, gpointer user_data)
-{
-    return _vlan_qos_mapping_cmp_from(*((const NMVlanQosMapping **) a),
-                                      *((const NMVlanQosMapping **) b),
-                                      NULL);
-}
-
-/******************************************************************
- * NMLinkType functions
- ******************************************************************/
-
-typedef struct {
-    const char *type_string;
-
-    /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type
-     * should only be specified if the device type can be created without
-     * additional parameters, and if the device type can be determined from
-     * the rtnl_type.  eg, tun/tap should not be specified since both
-     * tun and tap devices use "tun", and InfiniBand should not be
-     * specified because a PKey is required at creation. Drivers set this
-     * value from their 'struct rtnl_link_ops' structure.
-     */
-    const char *rtnl_type;
-
-    /* uevent DEVTYPE where applicable, from /sys/class/net/<ifname>/uevent;
-     * drivers set this value from their SET_NETDEV_DEV() call and the
-     * 'struct device_type' name member.
-     */
-    const char *devtype;
-} LinkDesc;
-
-static const LinkDesc link_descs[] = {
-    [NM_LINK_TYPE_NONE]    = {"none", NULL, NULL},
-    [NM_LINK_TYPE_UNKNOWN] = {"unknown", NULL, NULL},
-    [NM_LINK_TYPE_ANY]     = {"any", NULL, NULL},
-
-    [NM_LINK_TYPE_ETHERNET]   = {"ethernet", NULL, NULL},
-    [NM_LINK_TYPE_INFINIBAND] = {"infiniband", NULL, NULL},
-    [NM_LINK_TYPE_OLPC_MESH]  = {"olpc-mesh", NULL, NULL},
-    [NM_LINK_TYPE_WIFI]       = {"wifi", NULL, "wlan"},
-    [NM_LINK_TYPE_WWAN_NET]   = {"wwan", NULL, "wwan"},
-    [NM_LINK_TYPE_WIMAX]      = {"wimax", "wimax", "wimax"},
-    [NM_LINK_TYPE_WPAN]       = {"wpan", NULL, NULL},
-    [NM_LINK_TYPE_6LOWPAN]    = {"6lowpan", NULL, NULL},
-
-    [NM_LINK_TYPE_BNEP]        = {"bluetooth", NULL, "bluetooth"},
-    [NM_LINK_TYPE_DUMMY]       = {"dummy", "dummy", NULL},
-    [NM_LINK_TYPE_GRE]         = {"gre", "gre", NULL},
-    [NM_LINK_TYPE_GRETAP]      = {"gretap", "gretap", NULL},
-    [NM_LINK_TYPE_IFB]         = {"ifb", "ifb", NULL},
-    [NM_LINK_TYPE_IP6TNL]      = {"ip6tnl", "ip6tnl", NULL},
-    [NM_LINK_TYPE_IP6GRE]      = {"ip6gre", "ip6gre", NULL},
-    [NM_LINK_TYPE_IP6GRETAP]   = {"ip6gretap", "ip6gretap", NULL},
-    [NM_LINK_TYPE_IPIP]        = {"ipip", "ipip", NULL},
-    [NM_LINK_TYPE_LOOPBACK]    = {"loopback", NULL, NULL},
-    [NM_LINK_TYPE_MACSEC]      = {"macsec", "macsec", NULL},
-    [NM_LINK_TYPE_MACVLAN]     = {"macvlan", "macvlan", NULL},
-    [NM_LINK_TYPE_MACVTAP]     = {"macvtap", "macvtap", NULL},
-    [NM_LINK_TYPE_OPENVSWITCH] = {"openvswitch", "openvswitch", NULL},
-    [NM_LINK_TYPE_PPP]         = {"ppp", NULL, "ppp"},
-    [NM_LINK_TYPE_SIT]         = {"sit", "sit", NULL},
-    [NM_LINK_TYPE_TUN]         = {"tun", "tun", NULL},
-    [NM_LINK_TYPE_VETH]        = {"veth", "veth", NULL},
-    [NM_LINK_TYPE_VLAN]        = {"vlan", "vlan", "vlan"},
-    [NM_LINK_TYPE_VRF]         = {"vrf", "vrf", "vrf"},
-    [NM_LINK_TYPE_VXLAN]       = {"vxlan", "vxlan", "vxlan"},
-    [NM_LINK_TYPE_WIREGUARD]   = {"wireguard", "wireguard", "wireguard"},
-
-    [NM_LINK_TYPE_BRIDGE] = {"bridge", "bridge", "bridge"},
-    [NM_LINK_TYPE_BOND]   = {"bond", "bond", "bond"},
-    [NM_LINK_TYPE_TEAM]   = {"team", "team", NULL},
-};
-
-static const LinkDesc *
-_link_desc_from_link_type(NMLinkType link_type)
-{
-    nm_assert(_NM_INT_NOT_NEGATIVE(link_type));
-    nm_assert(link_type < G_N_ELEMENTS(link_descs));
-    nm_assert(link_descs[link_type].type_string);
-
-    return &link_descs[link_type];
-}
-
-static NMLinkType
-_link_type_from_rtnl_type(const char *name)
-{
-    static const NMLinkType LIST[] = {
-        NM_LINK_TYPE_BOND,        /* "bond"        */
-        NM_LINK_TYPE_BRIDGE,      /* "bridge"      */
-        NM_LINK_TYPE_DUMMY,       /* "dummy"       */
-        NM_LINK_TYPE_GRE,         /* "gre"         */
-        NM_LINK_TYPE_GRETAP,      /* "gretap"      */
-        NM_LINK_TYPE_IFB,         /* "ifb"         */
-        NM_LINK_TYPE_IP6GRE,      /* "ip6gre"      */
-        NM_LINK_TYPE_IP6GRETAP,   /* "ip6gretap"   */
-        NM_LINK_TYPE_IP6TNL,      /* "ip6tnl"      */
-        NM_LINK_TYPE_IPIP,        /* "ipip"        */
-        NM_LINK_TYPE_MACSEC,      /* "macsec"      */
-        NM_LINK_TYPE_MACVLAN,     /* "macvlan"     */
-        NM_LINK_TYPE_MACVTAP,     /* "macvtap"     */
-        NM_LINK_TYPE_OPENVSWITCH, /* "openvswitch" */
-        NM_LINK_TYPE_SIT,         /* "sit"         */
-        NM_LINK_TYPE_TEAM,        /* "team"        */
-        NM_LINK_TYPE_TUN,         /* "tun"         */
-        NM_LINK_TYPE_VETH,        /* "veth"        */
-        NM_LINK_TYPE_VLAN,        /* "vlan"        */
-        NM_LINK_TYPE_VRF,         /* "vrf"         */
-        NM_LINK_TYPE_VXLAN,       /* "vxlan"       */
-        NM_LINK_TYPE_WIMAX,       /* "wimax"       */
-        NM_LINK_TYPE_WIREGUARD,   /* "wireguard"   */
-    };
-
-    nm_assert(name);
-
-    if (NM_MORE_ASSERT_ONCE(5)) {
-        int i, j, k;
-
-        for (i = 0; i < G_N_ELEMENTS(LIST); i++) {
-            nm_assert(_link_desc_from_link_type(LIST[i]) == &link_descs[LIST[i]]);
-            nm_assert(link_descs[LIST[i]].rtnl_type);
-            if (i > 0)
-                nm_assert(strcmp(link_descs[LIST[i - 1]].rtnl_type, link_descs[LIST[i]].rtnl_type)
-                          < 0);
-        }
-        for (i = 0; i < G_N_ELEMENTS(link_descs); i++) {
-            if (!link_descs[i].rtnl_type)
-                continue;
-            for (j = 0, k = 0; j < G_N_ELEMENTS(LIST); j++)
-                k += (LIST[j] == i);
-            nm_assert(k == 1);
-        }
-    }
-
-    {
-        int imin = 0;
-        int imax = (G_N_ELEMENTS(LIST) - 1);
-        int imid = (G_N_ELEMENTS(LIST) - 1) / 2;
-
-        for (;;) {
-            const int cmp = strcmp(link_descs[LIST[imid]].rtnl_type, name);
-
-            if (G_UNLIKELY(cmp == 0))
-                return LIST[imid];
-
-            if (cmp < 0)
-                imin = imid + 1;
-            else
-                imax = imid - 1;
-
-            if (G_UNLIKELY(imin > imax))
-                return NM_LINK_TYPE_NONE;
-
-            imid = (imin + imax) / 2;
-        }
-    }
-}
-
-static NMLinkType
-_link_type_from_devtype(const char *name)
-{
-    static const NMLinkType LIST[] = {
-        NM_LINK_TYPE_BNEP,      /* "bluetooth" */
-        NM_LINK_TYPE_BOND,      /* "bond"      */
-        NM_LINK_TYPE_BRIDGE,    /* "bridge"    */
-        NM_LINK_TYPE_PPP,       /* "ppp"       */
-        NM_LINK_TYPE_VLAN,      /* "vlan"      */
-        NM_LINK_TYPE_VRF,       /* "vrf"       */
-        NM_LINK_TYPE_VXLAN,     /* "vxlan"     */
-        NM_LINK_TYPE_WIMAX,     /* "wimax"     */
-        NM_LINK_TYPE_WIREGUARD, /* "wireguard" */
-        NM_LINK_TYPE_WIFI,      /* "wlan"      */
-        NM_LINK_TYPE_WWAN_NET,  /* "wwan"      */
-    };
-
-    nm_assert(name);
-
-    if (NM_MORE_ASSERT_ONCE(5)) {
-        int i, j, k;
-
-        for (i = 0; i < G_N_ELEMENTS(LIST); i++) {
-            nm_assert(_link_desc_from_link_type(LIST[i]) == &link_descs[LIST[i]]);
-            nm_assert(link_descs[LIST[i]].devtype);
-            if (i > 0)
-                nm_assert(strcmp(link_descs[LIST[i - 1]].devtype, link_descs[LIST[i]].devtype) < 0);
-        }
-        for (i = 0; i < G_N_ELEMENTS(link_descs); i++) {
-            if (!link_descs[i].devtype)
-                continue;
-            for (j = 0, k = 0; j < G_N_ELEMENTS(LIST); j++)
-                k += (LIST[j] == i);
-            nm_assert(k == 1);
-        }
-    }
-
-    {
-        int imin = 0;
-        int imax = (G_N_ELEMENTS(LIST) - 1);
-        int imid = (G_N_ELEMENTS(LIST) - 1) / 2;
-
-        for (;;) {
-            const int cmp = strcmp(link_descs[LIST[imid]].devtype, name);
-
-            if (G_UNLIKELY(cmp == 0))
-                return LIST[imid];
-
-            if (cmp < 0)
-                imin = imid + 1;
-            else
-                imax = imid - 1;
-
-            if (G_UNLIKELY(imin > imax))
-                return NM_LINK_TYPE_NONE;
-
-            imid = (imin + imax) / 2;
-        }
-    }
-}
-
-static const char *
-nm_link_type_to_rtnl_type_string(NMLinkType link_type)
-{
-    return _link_desc_from_link_type(link_type)->rtnl_type;
-}
-
-const char *
-nm_link_type_to_string(NMLinkType link_type)
-{
-    return _link_desc_from_link_type(link_type)->type_string;
-}
-
-/******************************************************************
- * Utilities
- ******************************************************************/
-
-/* _timestamp_nl_to_ms:
- * @timestamp_nl: a timestamp from ifa_cacheinfo.
- * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current
- * uptime and how often timestamp_nl wrapped.
- *
- * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds.
- * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts
- * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However,
- * the uint32 counter wraps every 497 days of uptime, so we have to compensate
- * for that. */
-static gint64
-_timestamp_nl_to_ms(guint32 timestamp_nl, gint64 monotonic_ms)
-{
-    const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100);
-    gint64       timestamp_nl_ms;
-
-    /* convert timestamp from 1/100th of a second to msec. */
-    timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100);
-
-    /* timestamp wraps every 497 days. Try to compensate for that.*/
-    if (timestamp_nl_ms > monotonic_ms) {
-        /* timestamp_nl_ms is in the future. Truncate it to *now* */
-        timestamp_nl_ms = monotonic_ms;
-    } else if (monotonic_ms >= WRAP_INTERVAL) {
-        timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL;
-        if (timestamp_nl_ms > monotonic_ms)
-            timestamp_nl_ms -= WRAP_INTERVAL;
-    }
-
-    return timestamp_nl_ms;
-}
-
-static guint32
-_addrtime_timestamp_to_nm(guint32 timestamp, gint32 *out_now_nm)
-{
-    gint64 now_nl;
-    gint64 now_nm;
-    gint64 result;
-
-    /* timestamp is unset. Default to 1. */
-    if (!timestamp) {
-        NM_SET_OUT(out_now_nm, 0);
-        return 1;
-    }
-
-    /* do all the calculations in milliseconds scale */
-
-    now_nm = nm_utils_get_monotonic_timestamp_msec();
-    now_nl = nm_utils_clock_gettime_msec(CLOCK_MONOTONIC);
-
-    nm_assert(now_nm >= 1000);
-    nm_assert(now_nl >= 0);
-
-    result = now_nm - (now_nl - _timestamp_nl_to_ms(timestamp, now_nl));
-
-    NM_SET_OUT(out_now_nm, now_nm / 1000);
-
-    /* converting the timestamp into nm_utils_get_monotonic_timestamp_msec() scale is
-     * a good guess but fails in the following situations:
-     *
-     * - If the address existed before start of the process, the timestamp in nm scale would
-     *   be negative or zero. In this case we default to 1.
-     * - during hibernation, the CLOCK_MONOTONIC/timestamp drifts from
-     *   nm_utils_get_monotonic_timestamp_msec() scale.
-     */
-    if (result <= 1000)
-        return 1;
-
-    if (result > now_nm)
-        return now_nm / 1000;
-
-    return result / 1000;
-}
-
-static guint32
-_addrtime_extend_lifetime(guint32 lifetime, guint32 seconds)
-{
-    guint64 v;
-
-    if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT || seconds == 0)
-        return lifetime;
-
-    v = (guint64) lifetime + (guint64) seconds;
-    return MIN(v, NM_PLATFORM_LIFETIME_PERMANENT - 1);
-}
-
-/* The rtnl_addr object contains relative lifetimes @valid and @preferred
- * that count in seconds, starting from the moment when the kernel constructed
- * the netlink message.
- *
- * There is also a field rtnl_addr_last_update_time(), which is the absolute
- * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address
- * was modified (wrapping every 497 days).
- * Immediately at the time when the address was last modified, #NOW and @last_update_time
- * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time.
- * However, this is not true in general. As time goes by, whenever kernel sends a new address
- * via netlink, the lifetimes keep counting down.
- **/
-static void
-_addrtime_get_lifetimes(guint32  timestamp,
-                        guint32  lifetime,
-                        guint32  preferred,
-                        guint32 *out_timestamp,
-                        guint32 *out_lifetime,
-                        guint32 *out_preferred)
-{
-    gint32 now;
-
-    if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT || preferred != NM_PLATFORM_LIFETIME_PERMANENT) {
-        if (preferred > lifetime)
-            preferred = lifetime;
-        timestamp = _addrtime_timestamp_to_nm(timestamp, &now);
-
-        if (now == 0) {
-            /* strange. failed to detect the last-update time and assumed that timestamp is 1. */
-            nm_assert(timestamp == 1);
-            now = nm_utils_get_monotonic_timestamp_sec();
-        }
-        if (timestamp < now) {
-            guint32 diff = now - timestamp;
-
-            lifetime  = _addrtime_extend_lifetime(lifetime, diff);
-            preferred = _addrtime_extend_lifetime(preferred, diff);
-        } else
-            nm_assert(timestamp == now);
-    } else
-        timestamp = 0;
-    *out_timestamp = timestamp;
-    *out_lifetime  = lifetime;
-    *out_preferred = preferred;
-}
-
-/*****************************************************************************/
-
-static const NMPObject *
-_lookup_cached_link(const NMPCache *  cache,
-                    int               ifindex,
-                    gboolean *        completed_from_cache,
-                    const NMPObject **link_cached)
-{
-    const NMPObject *obj;
-
-    nm_assert(completed_from_cache && link_cached);
-
-    if (!*completed_from_cache) {
-        obj = ifindex > 0 && cache ? nmp_cache_lookup_link(cache, ifindex) : NULL;
-
-        *link_cached          = obj;
-        *completed_from_cache = TRUE;
-    }
-    return *link_cached;
-}
-
-/*****************************************************************************/
-
-#define DEVTYPE_PREFIX "DEVTYPE="
-
-static char *
-_linktype_read_devtype(int dirfd)
-{
-    gs_free char *contents = NULL;
-    char *        cont, *end;
-
-    nm_assert(dirfd >= 0);
-
-    if (!nm_utils_file_get_contents(dirfd,
-                                    "uevent",
-                                    1 * 1024 * 1024,
-                                    NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-                                    &contents,
-                                    NULL,
-                                    NULL,
-                                    NULL))
-        return NULL;
-    for (cont = contents; cont; cont = end) {
-        end = strpbrk(cont, "\r\n");
-        if (end)
-            *end++ = '\0';
-        if (strncmp(cont, DEVTYPE_PREFIX, NM_STRLEN(DEVTYPE_PREFIX)) == 0) {
-            cont += NM_STRLEN(DEVTYPE_PREFIX);
-            memmove(contents, cont, strlen(cont) + 1);
-            return g_steal_pointer(&contents);
-        }
-    }
-    return NULL;
-}
-
-static NMLinkType
-_linktype_get_type(NMPlatform *      platform,
-                   const NMPCache *  cache,
-                   const char *      kind,
-                   int               ifindex,
-                   const char *      ifname,
-                   unsigned          flags,
-                   unsigned          arptype,
-                   gboolean *        completed_from_cache,
-                   const NMPObject **link_cached,
-                   const char **     out_kind)
-{
-    NMLinkType link_type;
-
-    NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform);
-    nm_assert(ifname);
-    nm_assert(_link_type_from_devtype("wlan") == NM_LINK_TYPE_WIFI);
-    nm_assert(_link_type_from_rtnl_type("bond") == NM_LINK_TYPE_BOND);
-
-    if (completed_from_cache) {
-        const NMPObject *obj;
-
-        obj = _lookup_cached_link(cache, ifindex, completed_from_cache, link_cached);
-
-        /* If we detected the link type before, we stick to that
-         * decision unless the "kind" or "name" changed. If "name" changed,
-         * it means that their type may not have been determined correctly
-         * due to race conditions while accessing sysfs.
-         *
-         * This way, we save additional ethtool/sysctl lookups, but moreover,
-         * we keep the linktype stable and don't change it as long as the link
-         * exists.
-         *
-         * Note that kernel *can* reuse the ifindex (on integer overflow, and
-         * when moving interface to other netns). Thus here there is a tiny potential
-         * of messing stuff up. */
-        if (obj && obj->_link.netlink.is_in_netlink
-            && !NM_IN_SET(obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE)
-            && nm_streq(ifname, obj->link.name) && (!kind || nm_streq0(kind, obj->link.kind))) {
-            nm_assert(obj->link.kind == g_intern_string(obj->link.kind));
-            *out_kind = obj->link.kind;
-            return obj->link.type;
-        }
-    }
-
-    /* we intern kind to not require us to keep the pointer alive. Essentially
-     * leaking it in a global cache. That should be safe enough, because the
-     * kind comes only from kernel messages, which depend on the number of
-     * available drivers. So, there is not the danger that we leak uncontrolled
-     * many kinds. */
-    *out_kind = g_intern_string(kind);
-
-    if (kind) {
-        link_type = _link_type_from_rtnl_type(kind);
-        if (link_type != NM_LINK_TYPE_NONE)
-            return link_type;
-    }
-
-    if (arptype == ARPHRD_LOOPBACK)
-        return NM_LINK_TYPE_LOOPBACK;
-    else if (arptype == ARPHRD_INFINIBAND)
-        return NM_LINK_TYPE_INFINIBAND;
-    else if (arptype == ARPHRD_SIT)
-        return NM_LINK_TYPE_SIT;
-    else if (arptype == ARPHRD_TUNNEL6)
-        return NM_LINK_TYPE_IP6TNL;
-    else if (arptype == ARPHRD_PPP)
-        return NM_LINK_TYPE_PPP;
-    else if (arptype == ARPHRD_IEEE802154)
-        return NM_LINK_TYPE_WPAN;
-    else if (arptype == ARPHRD_6LOWPAN)
-        return NM_LINK_TYPE_6LOWPAN;
-
-    {
-        NMPUtilsEthtoolDriverInfo driver_info;
-
-        /* Fallback OVS detection for kernel <= 3.16 */
-        if (nmp_utils_ethtool_get_driver_info(ifindex, &driver_info)) {
-            if (nm_streq(driver_info.driver, "openvswitch"))
-                return NM_LINK_TYPE_OPENVSWITCH;
-
-            if (arptype == 256) {
-                /* Some s390 CTC-type devices report 256 for the encapsulation type
-                 * for some reason, but we need to call them Ethernet.
-                 */
-                if (nm_streq(driver_info.driver, "ctcm"))
-                    return NM_LINK_TYPE_ETHERNET;
-            }
-        }
-    }
-
-    {
-        nm_auto_close int dirfd   = -1;
-        gs_free char *    devtype = NULL;
-        char              ifname_verified[IFNAMSIZ];
-
-        dirfd = nmp_utils_sysctl_open_netdir(ifindex, ifname, ifname_verified);
-        if (dirfd >= 0) {
-            if (faccessat(dirfd, "anycast_mask", F_OK, 0) == 0)
-                return NM_LINK_TYPE_OLPC_MESH;
-
-            devtype = _linktype_read_devtype(dirfd);
-            if (devtype) {
-                link_type = _link_type_from_devtype(devtype);
-                if (link_type != NM_LINK_TYPE_NONE) {
-                    if (link_type == NM_LINK_TYPE_BNEP && arptype != ARPHRD_ETHER) {
-                        /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must
-                         * use arptype to distinguish between them.
-                         */
-                    } else
-                        return link_type;
-                }
-            }
-
-            /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */
-            if (nm_wifi_utils_is_wifi(dirfd, ifname_verified))
-                return NM_LINK_TYPE_WIFI;
-        }
-
-        if (arptype == ARPHRD_ETHER) {
-            /* Misc non-upstream WWAN drivers.  rmnet is Qualcomm's proprietary
-             * modem interface, ccmni is MediaTek's.  FIXME: these drivers should
-             * really set devtype=WWAN.
-             */
-            if (g_str_has_prefix(ifname, "rmnet") || g_str_has_prefix(ifname, "rev_rmnet")
-                || g_str_has_prefix(ifname, "ccmni"))
-                return NM_LINK_TYPE_WWAN_NET;
-
-            /* Standard wired ethernet interfaces don't report an rtnl_link_type, so
-             * only allow fallback to Ethernet if no type is given.  This should
-             * prevent future virtual network drivers from being treated as Ethernet
-             * when they should be Generic instead.
-             */
-            if (!kind && !devtype)
-                return NM_LINK_TYPE_ETHERNET;
-
-            /* The USB gadget interfaces behave and look like ordinary ethernet devices
-             * aside from the DEVTYPE. */
-            if (nm_streq0(devtype, "gadget"))
-                return NM_LINK_TYPE_ETHERNET;
-
-            /* Distributed Switch Architecture switch chips */
-            if (nm_streq0(devtype, "dsa"))
-                return NM_LINK_TYPE_ETHERNET;
-        }
-    }
-
-    return NM_LINK_TYPE_UNKNOWN;
-}
-
-/******************************************************************
- * libnl unility functions and wrappers
- ******************************************************************/
-
-#define NLMSG_TAIL(nmsg) ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
-
-/* copied from iproute2's addattr_l(). */
-static gboolean
-_nl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen)
-{
-    int            len = RTA_LENGTH(alen);
-    struct rtattr *rta;
-
-    if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
-        return FALSE;
-
-    rta           = NLMSG_TAIL(n);
-    rta->rta_type = type;
-    rta->rta_len  = len;
-    memcpy(RTA_DATA(rta), data, alen);
-    n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
-    return TRUE;
-}
-
-/******************************************************************
- * NMPObject/netlink functions
- ******************************************************************/
-
-#define _check_addr_or_return_val(tb, attr, addr_len, ret_val) \
-    ({                                                         \
-        const struct nlattr *__t = (tb)[(attr)];               \
-                                                               \
-        if (__t) {                                             \
-            if (nla_len(__t) != (addr_len)) {                  \
-                return ret_val;                                \
-            }                                                  \
-        }                                                      \
-        !!__t;                                                 \
-    })
-
-#define _check_addr_or_return_null(tb, attr, addr_len) \
-    _check_addr_or_return_val(tb, attr, addr_len, NULL)
-
-/*****************************************************************************/
-
-/* Copied and heavily modified from libnl3's inet6_parse_protinfo(). */
-static gboolean
-_parse_af_inet6(NMPlatform *        platform,
-                struct nlattr *     attr,
-                NMUtilsIPv6IfaceId *out_token,
-                gboolean *          out_token_valid,
-                guint8 *            out_addr_gen_mode_inv,
-                gboolean *          out_addr_gen_mode_valid)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_INET6_FLAGS]      = {.type = NLA_U32},
-        [IFLA_INET6_CACHEINFO]  = {.minlen = nm_offsetofend(struct ifla_cacheinfo, retrans_time)},
-        [IFLA_INET6_CONF]       = {.minlen = 4},
-        [IFLA_INET6_STATS]      = {.minlen = 8},
-        [IFLA_INET6_ICMP6STATS] = {.minlen = 8},
-        [IFLA_INET6_TOKEN]      = {.minlen = sizeof(struct in6_addr)},
-        [IFLA_INET6_ADDR_GEN_MODE] = {.type = NLA_U8},
-    };
-    struct nlattr * tb[G_N_ELEMENTS(policy)];
-    struct in6_addr i6_token;
-    gboolean        token_valid          = FALSE;
-    gboolean        addr_gen_mode_valid  = FALSE;
-    guint8          i6_addr_gen_mode_inv = 0;
-
-    if (nla_parse_nested_arr(tb, attr, policy) < 0)
-        return FALSE;
-
-    if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4)
-        return FALSE;
-    if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8)
-        return FALSE;
-    if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8)
-        return FALSE;
-
-    if (_check_addr_or_return_val(tb, IFLA_INET6_TOKEN, sizeof(struct in6_addr), FALSE)) {
-        nla_memcpy(&i6_token, tb[IFLA_INET6_TOKEN], sizeof(struct in6_addr));
-        token_valid = TRUE;
-    }
-
-    /* Hack to detect support addrgenmode of the kernel. We only parse
-     * netlink messages that we receive from kernel, hence this check
-     * is valid. */
-    if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) {
-        /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */
-        _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL,
-                                         tb[IFLA_INET6_ADDR_GEN_MODE] ? 1 : -1);
-    }
-
-    if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
-        i6_addr_gen_mode_inv = _nm_platform_uint8_inv(nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]));
-        if (i6_addr_gen_mode_inv == 0) {
-            /* an inverse addrgenmode of zero is unexpected. We need to reserve zero
-             * to signal "unset". */
-            return FALSE;
-        }
-        addr_gen_mode_valid = TRUE;
-    }
-
-    if (token_valid) {
-        *out_token_valid = token_valid;
-        nm_utils_ipv6_interface_identifier_get_from_addr(out_token, &i6_token);
-    }
-    if (addr_gen_mode_valid) {
-        *out_addr_gen_mode_valid = addr_gen_mode_valid;
-        *out_addr_gen_mode_inv   = i6_addr_gen_mode_inv;
-    }
-    return TRUE;
-}
-
-/*****************************************************************************/
-
-static NMPObject *
-_parse_lnk_bridge(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_BR_FORWARD_DELAY]              = {.type = NLA_U32},
-        [IFLA_BR_HELLO_TIME]                 = {.type = NLA_U32},
-        [IFLA_BR_MAX_AGE]                    = {.type = NLA_U32},
-        [IFLA_BR_AGEING_TIME]                = {.type = NLA_U32},
-        [IFLA_BR_STP_STATE]                  = {.type = NLA_U32},
-        [IFLA_BR_PRIORITY]                   = {.type = NLA_U16},
-        [IFLA_BR_VLAN_PROTOCOL]              = {.type = NLA_U16},
-        [IFLA_BR_VLAN_STATS_ENABLED]         = {.type = NLA_U8},
-        [IFLA_BR_GROUP_FWD_MASK]             = {.type = NLA_U16},
-        [IFLA_BR_GROUP_ADDR]                 = {.minlen = sizeof(NMEtherAddr)},
-        [IFLA_BR_MCAST_SNOOPING]             = {.type = NLA_U8},
-        [IFLA_BR_MCAST_ROUTER]               = {.type = NLA_U8},
-        [IFLA_BR_MCAST_QUERY_USE_IFADDR]     = {.type = NLA_U8},
-        [IFLA_BR_MCAST_QUERIER]              = {.type = NLA_U8},
-        [IFLA_BR_MCAST_HASH_MAX]             = {.type = NLA_U32},
-        [IFLA_BR_MCAST_LAST_MEMBER_CNT]      = {.type = NLA_U32},
-        [IFLA_BR_MCAST_STARTUP_QUERY_CNT]    = {.type = NLA_U32},
-        [IFLA_BR_MCAST_LAST_MEMBER_INTVL]    = {.type = NLA_U64},
-        [IFLA_BR_MCAST_MEMBERSHIP_INTVL]     = {.type = NLA_U64},
-        [IFLA_BR_MCAST_QUERIER_INTVL]        = {.type = NLA_U64},
-        [IFLA_BR_MCAST_QUERY_INTVL]          = {.type = NLA_U64},
-        [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = {.type = NLA_U64},
-        [IFLA_BR_MCAST_STARTUP_QUERY_INTVL]  = {.type = NLA_U64},
-    };
-    NMPlatformLnkBridge *props;
-    struct nlattr *      tb[G_N_ELEMENTS(policy)];
-    NMPObject *          obj;
-
-    if (!info_data || !nm_streq0(kind, "bridge"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_BRIDGE, NULL);
-
-    props  = &obj->lnk_bridge;
-    *props = nm_platform_lnk_bridge_default;
-
-    if (!_nm_platform_kernel_support_detected(
-            NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED)) {
-        /* IFLA_BR_VLAN_STATS_ENABLED was added in kernel 4.10 on April 30, 2016.
-         * See commit 6dada9b10a0818ba72c249526a742c8c41274a73. */
-        _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED,
-                                         tb[IFLA_BR_VLAN_STATS_ENABLED] ? 1 : -1);
-    }
-
-    if (tb[IFLA_BR_FORWARD_DELAY])
-        props->forward_delay = nla_get_u32(tb[IFLA_BR_FORWARD_DELAY]);
-    if (tb[IFLA_BR_HELLO_TIME])
-        props->hello_time = nla_get_u32(tb[IFLA_BR_HELLO_TIME]);
-    if (tb[IFLA_BR_MAX_AGE])
-        props->max_age = nla_get_u32(tb[IFLA_BR_MAX_AGE]);
-    if (tb[IFLA_BR_AGEING_TIME])
-        props->ageing_time = nla_get_u32(tb[IFLA_BR_AGEING_TIME]);
-    if (tb[IFLA_BR_STP_STATE])
-        props->stp_state = !!nla_get_u32(tb[IFLA_BR_STP_STATE]);
-    if (tb[IFLA_BR_PRIORITY])
-        props->priority = nla_get_u16(tb[IFLA_BR_PRIORITY]);
-    if (tb[IFLA_BR_VLAN_PROTOCOL])
-        props->vlan_protocol = ntohs(nla_get_u16(tb[IFLA_BR_VLAN_PROTOCOL]));
-    if (tb[IFLA_BR_VLAN_STATS_ENABLED])
-        props->vlan_stats_enabled = nla_get_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]);
-    if (tb[IFLA_BR_GROUP_FWD_MASK])
-        props->group_fwd_mask = nla_get_u16(tb[IFLA_BR_GROUP_FWD_MASK]);
-    if (tb[IFLA_BR_GROUP_ADDR])
-        props->group_addr = *nla_data_as(NMEtherAddr, tb[IFLA_BR_GROUP_ADDR]);
-    if (tb[IFLA_BR_MCAST_SNOOPING])
-        props->mcast_snooping = !!nla_get_u8(tb[IFLA_BR_MCAST_SNOOPING]);
-    if (tb[IFLA_BR_MCAST_ROUTER])
-        props->mcast_router = nla_get_u8(tb[IFLA_BR_MCAST_ROUTER]);
-    if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])
-        props->mcast_query_use_ifaddr = !!nla_get_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]);
-    if (tb[IFLA_BR_MCAST_QUERIER])
-        props->mcast_querier = nla_get_u8(tb[IFLA_BR_MCAST_QUERIER]);
-    if (tb[IFLA_BR_MCAST_HASH_MAX])
-        props->mcast_hash_max = nla_get_u32(tb[IFLA_BR_MCAST_HASH_MAX]);
-    if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])
-        props->mcast_last_member_count = nla_get_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]);
-    if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])
-        props->mcast_startup_query_count = nla_get_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]);
-    if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])
-        props->mcast_last_member_interval = nla_get_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]);
-    if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])
-        props->mcast_membership_interval = nla_get_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]);
-    if (tb[IFLA_BR_MCAST_QUERIER_INTVL])
-        props->mcast_querier_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL]);
-    if (tb[IFLA_BR_MCAST_QUERY_INTVL])
-        props->mcast_query_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERY_INTVL]);
-    if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])
-        props->mcast_query_response_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]);
-    if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])
-        props->mcast_startup_query_interval = nla_get_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]);
-
-    return obj;
-}
-
-/***********************************************************************************/
-
-static NMPObject *
-_parse_lnk_gre(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_GRE_LINK]     = {.type = NLA_U32},
-        [IFLA_GRE_IFLAGS]   = {.type = NLA_U16},
-        [IFLA_GRE_OFLAGS]   = {.type = NLA_U16},
-        [IFLA_GRE_IKEY]     = {.type = NLA_U32},
-        [IFLA_GRE_OKEY]     = {.type = NLA_U32},
-        [IFLA_GRE_LOCAL]    = {.type = NLA_U32},
-        [IFLA_GRE_REMOTE]   = {.type = NLA_U32},
-        [IFLA_GRE_TTL]      = {.type = NLA_U8},
-        [IFLA_GRE_TOS]      = {.type = NLA_U8},
-        [IFLA_GRE_PMTUDISC] = {.type = NLA_U8},
-    };
-    struct nlattr *   tb[G_N_ELEMENTS(policy)];
-    NMPObject *       obj;
-    NMPlatformLnkGre *props;
-    gboolean          is_tap;
-
-    if (!info_data || !kind)
-        return NULL;
-
-    if (nm_streq(kind, "gretap"))
-        is_tap = TRUE;
-    else if (nm_streq(kind, "gre"))
-        is_tap = FALSE;
-    else
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj   = nmp_object_new(is_tap ? NMP_OBJECT_TYPE_LNK_GRETAP : NMP_OBJECT_TYPE_LNK_GRE, NULL);
-    props = &obj->lnk_gre;
-
-    props->parent_ifindex     = tb[IFLA_GRE_LINK] ? nla_get_u32(tb[IFLA_GRE_LINK]) : 0;
-    props->input_flags        = tb[IFLA_GRE_IFLAGS] ? ntohs(nla_get_u16(tb[IFLA_GRE_IFLAGS])) : 0;
-    props->output_flags       = tb[IFLA_GRE_OFLAGS] ? ntohs(nla_get_u16(tb[IFLA_GRE_OFLAGS])) : 0;
-    props->input_key          = tb[IFLA_GRE_IKEY] ? ntohl(nla_get_u32(tb[IFLA_GRE_IKEY])) : 0;
-    props->output_key         = tb[IFLA_GRE_OKEY] ? ntohl(nla_get_u32(tb[IFLA_GRE_OKEY])) : 0;
-    props->local              = tb[IFLA_GRE_LOCAL] ? nla_get_u32(tb[IFLA_GRE_LOCAL]) : 0;
-    props->remote             = tb[IFLA_GRE_REMOTE] ? nla_get_u32(tb[IFLA_GRE_REMOTE]) : 0;
-    props->tos                = tb[IFLA_GRE_TOS] ? nla_get_u8(tb[IFLA_GRE_TOS]) : 0;
-    props->ttl                = tb[IFLA_GRE_TTL] ? nla_get_u8(tb[IFLA_GRE_TTL]) : 0;
-    props->path_mtu_discovery = !tb[IFLA_GRE_PMTUDISC] || !!nla_get_u8(tb[IFLA_GRE_PMTUDISC]);
-    props->is_tap             = is_tap;
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-/* IFLA_IPOIB_* were introduced in the 3.7 kernel, but the kernel headers
- * we're building against might not have those properties even though the
- * running kernel might.
- */
-#define IFLA_IPOIB_UNSPEC 0
-#define IFLA_IPOIB_PKEY   1
-#define IFLA_IPOIB_MODE   2
-#define IFLA_IPOIB_UMCAST 3
-#undef IFLA_IPOIB_MAX
-#define IFLA_IPOIB_MAX IFLA_IPOIB_UMCAST
-
-#define IPOIB_MODE_DATAGRAM  0 /* using unreliable datagram QPs */
-#define IPOIB_MODE_CONNECTED 1 /* using connected QPs */
-
-static NMPObject *
-_parse_lnk_infiniband(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_IPOIB_PKEY]   = {.type = NLA_U16},
-        [IFLA_IPOIB_MODE]   = {.type = NLA_U16},
-        [IFLA_IPOIB_UMCAST] = {.type = NLA_U16},
-    };
-    struct nlattr *          tb[G_N_ELEMENTS(policy)];
-    NMPlatformLnkInfiniband *info;
-    NMPObject *              obj;
-    const char *             mode;
-
-    if (!info_data || !nm_streq0(kind, "ipoib"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE])
-        return NULL;
-
-    switch (nla_get_u16(tb[IFLA_IPOIB_MODE])) {
-    case IPOIB_MODE_DATAGRAM:
-        mode = "datagram";
-        break;
-    case IPOIB_MODE_CONNECTED:
-        mode = "connected";
-        break;
-    default:
-        return NULL;
-    }
-
-    obj  = nmp_object_new(NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL);
-    info = &obj->lnk_infiniband;
-
-    info->p_key = nla_get_u16(tb[IFLA_IPOIB_PKEY]);
-    info->mode  = mode;
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-static NMPObject *
-_parse_lnk_ip6tnl(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_IPTUN_LINK]        = {.type = NLA_U32},
-        [IFLA_IPTUN_LOCAL]       = {.minlen = sizeof(struct in6_addr)},
-        [IFLA_IPTUN_REMOTE]      = {.minlen = sizeof(struct in6_addr)},
-        [IFLA_IPTUN_TTL]         = {.type = NLA_U8},
-        [IFLA_IPTUN_ENCAP_LIMIT] = {.type = NLA_U8},
-        [IFLA_IPTUN_FLOWINFO]    = {.type = NLA_U32},
-        [IFLA_IPTUN_PROTO]       = {.type = NLA_U8},
-        [IFLA_IPTUN_FLAGS]       = {.type = NLA_U32},
-    };
-    struct nlattr *      tb[G_N_ELEMENTS(policy)];
-    NMPObject *          obj;
-    NMPlatformLnkIp6Tnl *props;
-    guint32              flowinfo;
-
-    if (!info_data || !nm_streq0(kind, "ip6tnl"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj   = nmp_object_new(NMP_OBJECT_TYPE_LNK_IP6TNL, NULL);
-    props = &obj->lnk_ip6tnl;
-
-    if (tb[IFLA_IPTUN_LINK])
-        props->parent_ifindex = nla_get_u32(tb[IFLA_IPTUN_LINK]);
-    if (tb[IFLA_IPTUN_LOCAL])
-        props->local = *nla_data_as(struct in6_addr, tb[IFLA_IPTUN_LOCAL]);
-    if (tb[IFLA_IPTUN_REMOTE])
-        props->remote = *nla_data_as(struct in6_addr, tb[IFLA_IPTUN_REMOTE]);
-    if (tb[IFLA_IPTUN_TTL])
-        props->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]);
-    if (tb[IFLA_IPTUN_ENCAP_LIMIT])
-        props->encap_limit = nla_get_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]);
-    if (tb[IFLA_IPTUN_FLOWINFO]) {
-        flowinfo          = ntohl(nla_get_u32(tb[IFLA_IPTUN_FLOWINFO]));
-        props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK;
-        props->tclass     = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT;
-    }
-    if (tb[IFLA_IPTUN_PROTO])
-        props->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]);
-    if (tb[IFLA_IPTUN_FLAGS])
-        props->flags = nla_get_u32(tb[IFLA_IPTUN_FLAGS]);
-
-    return obj;
-}
-
-static NMPObject *
-_parse_lnk_ip6gre(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_GRE_LINK]        = {.type = NLA_U32},
-        [IFLA_GRE_IFLAGS]      = {.type = NLA_U16},
-        [IFLA_GRE_OFLAGS]      = {.type = NLA_U16},
-        [IFLA_GRE_IKEY]        = {.type = NLA_U32},
-        [IFLA_GRE_OKEY]        = {.type = NLA_U32},
-        [IFLA_GRE_LOCAL]       = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)},
-        [IFLA_GRE_REMOTE]      = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)},
-        [IFLA_GRE_TTL]         = {.type = NLA_U8},
-        [IFLA_GRE_ENCAP_LIMIT] = {.type = NLA_U8},
-        [IFLA_GRE_FLOWINFO]    = {.type = NLA_U32},
-        [IFLA_GRE_FLAGS]       = {.type = NLA_U32},
-    };
-    struct nlattr *      tb[G_N_ELEMENTS(policy)];
-    NMPObject *          obj;
-    NMPlatformLnkIp6Tnl *props;
-    guint32              flowinfo;
-    gboolean             is_tap;
-
-    if (!info_data || !kind)
-        return NULL;
-
-    if (nm_streq(kind, "ip6gre"))
-        is_tap = FALSE;
-    else if (nm_streq(kind, "ip6gretap"))
-        is_tap = TRUE;
-    else
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj = nmp_object_new(is_tap ? NMP_OBJECT_TYPE_LNK_IP6GRETAP : NMP_OBJECT_TYPE_LNK_IP6GRE, NULL);
-    props         = &obj->lnk_ip6tnl;
-    props->is_gre = TRUE;
-    props->is_tap = is_tap;
-
-    if (tb[IFLA_GRE_LINK])
-        props->parent_ifindex = nla_get_u32(tb[IFLA_GRE_LINK]);
-    if (tb[IFLA_GRE_IFLAGS])
-        props->input_flags = ntohs(nla_get_u16(tb[IFLA_GRE_IFLAGS]));
-    if (tb[IFLA_GRE_OFLAGS])
-        props->output_flags = ntohs(nla_get_u16(tb[IFLA_GRE_OFLAGS]));
-    if (tb[IFLA_GRE_IKEY])
-        props->input_key = ntohl(nla_get_u32(tb[IFLA_GRE_IKEY]));
-    if (tb[IFLA_GRE_OKEY])
-        props->output_key = ntohl(nla_get_u32(tb[IFLA_GRE_OKEY]));
-    if (tb[IFLA_GRE_LOCAL])
-        props->local = *nla_data_as(struct in6_addr, tb[IFLA_GRE_LOCAL]);
-    if (tb[IFLA_GRE_REMOTE])
-        props->remote = *nla_data_as(struct in6_addr, tb[IFLA_GRE_REMOTE]);
-    if (tb[IFLA_GRE_TTL])
-        props->ttl = nla_get_u8(tb[IFLA_GRE_TTL]);
-    if (tb[IFLA_GRE_ENCAP_LIMIT])
-        props->encap_limit = nla_get_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
-    if (tb[IFLA_GRE_FLOWINFO]) {
-        flowinfo          = ntohl(nla_get_u32(tb[IFLA_GRE_FLOWINFO]));
-        props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK;
-        props->tclass     = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT;
-    }
-    if (tb[IFLA_GRE_FLAGS])
-        props->flags = nla_get_u32(tb[IFLA_GRE_FLAGS]);
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-static NMPObject *
-_parse_lnk_ipip(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_IPTUN_LINK]     = {.type = NLA_U32},
-        [IFLA_IPTUN_LOCAL]    = {.type = NLA_U32},
-        [IFLA_IPTUN_REMOTE]   = {.type = NLA_U32},
-        [IFLA_IPTUN_TTL]      = {.type = NLA_U8},
-        [IFLA_IPTUN_TOS]      = {.type = NLA_U8},
-        [IFLA_IPTUN_PMTUDISC] = {.type = NLA_U8},
-    };
-    struct nlattr *    tb[G_N_ELEMENTS(policy)];
-    NMPObject *        obj;
-    NMPlatformLnkIpIp *props;
-
-    if (!info_data || !nm_streq0(kind, "ipip"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj   = nmp_object_new(NMP_OBJECT_TYPE_LNK_IPIP, NULL);
-    props = &obj->lnk_ipip;
-
-    props->parent_ifindex     = tb[IFLA_IPTUN_LINK] ? nla_get_u32(tb[IFLA_IPTUN_LINK]) : 0;
-    props->local              = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32(tb[IFLA_IPTUN_LOCAL]) : 0;
-    props->remote             = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32(tb[IFLA_IPTUN_REMOTE]) : 0;
-    props->tos                = tb[IFLA_IPTUN_TOS] ? nla_get_u8(tb[IFLA_IPTUN_TOS]) : 0;
-    props->ttl                = tb[IFLA_IPTUN_TTL] ? nla_get_u8(tb[IFLA_IPTUN_TTL]) : 0;
-    props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]);
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-static NMPObject *
-_parse_lnk_macvlan(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_MACVLAN_MODE]  = {.type = NLA_U32},
-        [IFLA_MACVLAN_FLAGS] = {.type = NLA_U16},
-    };
-    NMPlatformLnkMacvlan *props;
-    struct nlattr *       tb[G_N_ELEMENTS(policy)];
-    NMPObject *           obj;
-    gboolean              tap;
-
-    if (!info_data || !kind)
-        return NULL;
-
-    if (nm_streq(kind, "macvlan"))
-        tap = FALSE;
-    else if (nm_streq(kind, "macvtap"))
-        tap = TRUE;
-    else
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    if (!tb[IFLA_MACVLAN_MODE])
-        return NULL;
-
-    obj   = nmp_object_new(tap ? NMP_OBJECT_TYPE_LNK_MACVTAP : NMP_OBJECT_TYPE_LNK_MACVLAN, NULL);
-    props = &obj->lnk_macvlan;
-    props->mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]);
-    props->tap  = tap;
-
-    if (tb[IFLA_MACVLAN_FLAGS])
-        props->no_promisc =
-            NM_FLAGS_HAS(nla_get_u16(tb[IFLA_MACVLAN_FLAGS]), MACVLAN_FLAG_NOPROMISC);
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-static NMPObject *
-_parse_lnk_macsec(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_MACSEC_SCI]            = {.type = NLA_U64},
-        [IFLA_MACSEC_ICV_LEN]        = {.type = NLA_U8},
-        [IFLA_MACSEC_CIPHER_SUITE]   = {.type = NLA_U64},
-        [IFLA_MACSEC_WINDOW]         = {.type = NLA_U32},
-        [IFLA_MACSEC_ENCODING_SA]    = {.type = NLA_U8},
-        [IFLA_MACSEC_ENCRYPT]        = {.type = NLA_U8},
-        [IFLA_MACSEC_PROTECT]        = {.type = NLA_U8},
-        [IFLA_MACSEC_INC_SCI]        = {.type = NLA_U8},
-        [IFLA_MACSEC_ES]             = {.type = NLA_U8},
-        [IFLA_MACSEC_SCB]            = {.type = NLA_U8},
-        [IFLA_MACSEC_REPLAY_PROTECT] = {.type = NLA_U8},
-        [IFLA_MACSEC_VALIDATION]     = {.type = NLA_U8},
-    };
-    struct nlattr *      tb[G_N_ELEMENTS(policy)];
-    NMPObject *          obj;
-    NMPlatformLnkMacsec *props;
-
-    if (!info_data || !nm_streq0(kind, "macsec"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj   = nmp_object_new(NMP_OBJECT_TYPE_LNK_MACSEC, NULL);
-    props = &obj->lnk_macsec;
-
-    if (tb[IFLA_MACSEC_SCI]) {
-        props->sci = nla_get_be64(tb[IFLA_MACSEC_SCI]);
-    }
-    if (tb[IFLA_MACSEC_ICV_LEN]) {
-        props->icv_length = nla_get_u8(tb[IFLA_MACSEC_ICV_LEN]);
-    }
-    if (tb[IFLA_MACSEC_CIPHER_SUITE]) {
-        props->cipher_suite = nla_get_u64(tb[IFLA_MACSEC_CIPHER_SUITE]);
-    }
-    if (tb[IFLA_MACSEC_WINDOW]) {
-        props->window = nla_get_u32(tb[IFLA_MACSEC_WINDOW]);
-    }
-    if (tb[IFLA_MACSEC_ENCODING_SA]) {
-        props->encoding_sa = !!nla_get_u8(tb[IFLA_MACSEC_ENCODING_SA]);
-    }
-    if (tb[IFLA_MACSEC_ENCRYPT]) {
-        props->encrypt = !!nla_get_u8(tb[IFLA_MACSEC_ENCRYPT]);
-    }
-    if (tb[IFLA_MACSEC_PROTECT]) {
-        props->protect = !!nla_get_u8(tb[IFLA_MACSEC_PROTECT]);
-    }
-    if (tb[IFLA_MACSEC_INC_SCI]) {
-        props->include_sci = !!nla_get_u8(tb[IFLA_MACSEC_INC_SCI]);
-    }
-    if (tb[IFLA_MACSEC_ES]) {
-        props->es = !!nla_get_u8(tb[IFLA_MACSEC_ES]);
-    }
-    if (tb[IFLA_MACSEC_SCB]) {
-        props->scb = !!nla_get_u8(tb[IFLA_MACSEC_SCB]);
-    }
-    if (tb[IFLA_MACSEC_REPLAY_PROTECT]) {
-        props->replay_protect = !!nla_get_u8(tb[IFLA_MACSEC_REPLAY_PROTECT]);
-    }
-    if (tb[IFLA_MACSEC_VALIDATION]) {
-        props->validation = nla_get_u8(tb[IFLA_MACSEC_VALIDATION]);
-    }
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-static NMPObject *
-_parse_lnk_sit(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_IPTUN_LINK]     = {.type = NLA_U32},
-        [IFLA_IPTUN_LOCAL]    = {.type = NLA_U32},
-        [IFLA_IPTUN_REMOTE]   = {.type = NLA_U32},
-        [IFLA_IPTUN_TTL]      = {.type = NLA_U8},
-        [IFLA_IPTUN_TOS]      = {.type = NLA_U8},
-        [IFLA_IPTUN_PMTUDISC] = {.type = NLA_U8},
-        [IFLA_IPTUN_FLAGS]    = {.type = NLA_U16},
-        [IFLA_IPTUN_PROTO]    = {.type = NLA_U8},
-    };
-    struct nlattr *   tb[G_N_ELEMENTS(policy)];
-    NMPObject *       obj;
-    NMPlatformLnkSit *props;
-
-    if (!info_data || !nm_streq0(kind, "sit"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj   = nmp_object_new(NMP_OBJECT_TYPE_LNK_SIT, NULL);
-    props = &obj->lnk_sit;
-
-    props->parent_ifindex     = tb[IFLA_IPTUN_LINK] ? nla_get_u32(tb[IFLA_IPTUN_LINK]) : 0;
-    props->local              = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32(tb[IFLA_IPTUN_LOCAL]) : 0;
-    props->remote             = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32(tb[IFLA_IPTUN_REMOTE]) : 0;
-    props->tos                = tb[IFLA_IPTUN_TOS] ? nla_get_u8(tb[IFLA_IPTUN_TOS]) : 0;
-    props->ttl                = tb[IFLA_IPTUN_TTL] ? nla_get_u8(tb[IFLA_IPTUN_TTL]) : 0;
-    props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]);
-    props->flags              = tb[IFLA_IPTUN_FLAGS] ? nla_get_u16(tb[IFLA_IPTUN_FLAGS]) : 0;
-    props->proto              = tb[IFLA_IPTUN_PROTO] ? nla_get_u8(tb[IFLA_IPTUN_PROTO]) : 0;
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-static NMPObject *
-_parse_lnk_tun(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_TUN_OWNER]               = {.type = NLA_U32},
-        [IFLA_TUN_GROUP]               = {.type = NLA_U32},
-        [IFLA_TUN_TYPE]                = {.type = NLA_U8},
-        [IFLA_TUN_PI]                  = {.type = NLA_U8},
-        [IFLA_TUN_VNET_HDR]            = {.type = NLA_U8},
-        [IFLA_TUN_PERSIST]             = {.type = NLA_U8},
-        [IFLA_TUN_MULTI_QUEUE]         = {.type = NLA_U8},
-        [IFLA_TUN_NUM_QUEUES]          = {.type = NLA_U32},
-        [IFLA_TUN_NUM_DISABLED_QUEUES] = {.type = NLA_U32},
-    };
-    struct nlattr *   tb[G_N_ELEMENTS(policy)];
-    NMPObject *       obj;
-    NMPlatformLnkTun *props;
-
-    if (!info_data || !nm_streq0(kind, "tun"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    if (!tb[IFLA_TUN_TYPE])
-        return NULL;
-
-    obj   = nmp_object_new(NMP_OBJECT_TYPE_LNK_TUN, NULL);
-    props = &obj->lnk_tun;
-
-    props->type = nla_get_u8(tb[IFLA_TUN_TYPE]);
-
-    props->pi          = !!nla_get_u8_cond(tb, IFLA_TUN_PI, FALSE);
-    props->vnet_hdr    = !!nla_get_u8_cond(tb, IFLA_TUN_VNET_HDR, FALSE);
-    props->multi_queue = !!nla_get_u8_cond(tb, IFLA_TUN_MULTI_QUEUE, FALSE);
-    props->persist     = !!nla_get_u8_cond(tb, IFLA_TUN_PERSIST, FALSE);
-
-    if (tb[IFLA_TUN_OWNER]) {
-        props->owner_valid = TRUE;
-        props->owner       = nla_get_u32(tb[IFLA_TUN_OWNER]);
-    }
-    if (tb[IFLA_TUN_GROUP]) {
-        props->group_valid = TRUE;
-        props->group       = nla_get_u32(tb[IFLA_TUN_GROUP]);
-    }
-    return obj;
-}
-
-/*****************************************************************************/
-
-static gboolean
-_vlan_qos_mapping_from_nla(struct nlattr *          nlattr,
-                           const NMVlanQosMapping **out_map,
-                           guint *                  out_n_map)
-{
-    struct nlattr *   nla;
-    int               remaining;
-    gs_unref_ptrarray GPtrArray *array = NULL;
-
-    G_STATIC_ASSERT(sizeof(NMVlanQosMapping) == sizeof(struct ifla_vlan_qos_mapping));
-    G_STATIC_ASSERT(sizeof(((NMVlanQosMapping *) 0)->to)
-                    == sizeof(((struct ifla_vlan_qos_mapping *) 0)->to));
-    G_STATIC_ASSERT(sizeof(((NMVlanQosMapping *) 0)->from)
-                    == sizeof(((struct ifla_vlan_qos_mapping *) 0)->from));
-    G_STATIC_ASSERT(sizeof(NMVlanQosMapping)
-                    == sizeof(((NMVlanQosMapping *) 0)->from)
-                           + sizeof(((NMVlanQosMapping *) 0)->to));
-
-    nm_assert(out_map && !*out_map);
-    nm_assert(out_n_map && !*out_n_map);
-
-    if (!nlattr)
-        return TRUE;
-
-    array = g_ptr_array_new();
-    nla_for_each_nested (nla, nlattr, remaining) {
-        if (nla_len(nla) < sizeof(NMVlanQosMapping))
-            return FALSE;
-        g_ptr_array_add(array, nla_data(nla));
-    }
-
-    if (array->len > 0) {
-        NMVlanQosMapping *list;
-        guint             i, j;
-
-        /* The sorting is necessary, because for egress mapping, kernel
-         * doesn't sent the items strictly sorted by the from field. */
-        g_ptr_array_sort_with_data(array, _vlan_qos_mapping_cmp_from_ptr, NULL);
-
-        list = g_new(NMVlanQosMapping, array->len);
-
-        for (i = 0, j = 0; i < array->len; i++) {
-            NMVlanQosMapping *map;
-
-            map = array->pdata[i];
-
-            /* kernel doesn't really send us duplicates. Just be extra cautious
-             * because we want strong guarantees about the sort order and uniqueness
-             * of our mapping list (for simpler equality comparison). */
-            if (j > 0 && list[j - 1].from == map->from)
-                list[j - 1] = *map;
-            else
-                list[j++] = *map;
-        }
-
-        *out_n_map = j;
-        *out_map   = list;
-    }
-
-    return TRUE;
-}
-
-/* Copied and heavily modified from libnl3's vlan_parse() */
-static NMPObject *
-_parse_lnk_vlan(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_VLAN_ID]          = {.type = NLA_U16},
-        [IFLA_VLAN_FLAGS]       = {.minlen = nm_offsetofend(struct ifla_vlan_flags, flags)},
-        [IFLA_VLAN_INGRESS_QOS] = {.type = NLA_NESTED},
-        [IFLA_VLAN_EGRESS_QOS]  = {.type = NLA_NESTED},
-        [IFLA_VLAN_PROTOCOL]    = {.type = NLA_U16},
-    };
-    struct nlattr *tb[G_N_ELEMENTS(policy)];
-    nm_auto_nmpobj NMPObject *obj = NULL;
-    NMPObject *               obj_result;
-
-    if (!info_data || !nm_streq0(kind, "vlan"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    if (!tb[IFLA_VLAN_ID])
-        return NULL;
-
-    obj              = nmp_object_new(NMP_OBJECT_TYPE_LNK_VLAN, NULL);
-    obj->lnk_vlan.id = nla_get_u16(tb[IFLA_VLAN_ID]);
-
-    if (tb[IFLA_VLAN_FLAGS]) {
-        struct ifla_vlan_flags flags;
-
-        nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
-
-        obj->lnk_vlan.flags = flags.flags;
-    }
-
-    if (!_vlan_qos_mapping_from_nla(tb[IFLA_VLAN_INGRESS_QOS],
-                                    &obj->_lnk_vlan.ingress_qos_map,
-                                    &obj->_lnk_vlan.n_ingress_qos_map))
-        return NULL;
-
-    if (!_vlan_qos_mapping_from_nla(tb[IFLA_VLAN_EGRESS_QOS],
-                                    &obj->_lnk_vlan.egress_qos_map,
-                                    &obj->_lnk_vlan.n_egress_qos_map))
-        return NULL;
-
-    obj_result = obj;
-    obj        = NULL;
-    return obj_result;
-}
-
-/*****************************************************************************/
-
-/* The installed kernel headers might not have VXLAN stuff at all, or
- * they might have the original properties, but not PORT, GROUP6, or LOCAL6.
- * So until we depend on kernel >= 3.11, we just ignore the actual enum
- * in if_link.h and define the values ourselves.
- */
-#define IFLA_VXLAN_UNSPEC     0
-#define IFLA_VXLAN_ID         1
-#define IFLA_VXLAN_GROUP      2
-#define IFLA_VXLAN_LINK       3
-#define IFLA_VXLAN_LOCAL      4
-#define IFLA_VXLAN_TTL        5
-#define IFLA_VXLAN_TOS        6
-#define IFLA_VXLAN_LEARNING   7
-#define IFLA_VXLAN_AGEING     8
-#define IFLA_VXLAN_LIMIT      9
-#define IFLA_VXLAN_PORT_RANGE 10
-#define IFLA_VXLAN_PROXY      11
-#define IFLA_VXLAN_RSC        12
-#define IFLA_VXLAN_L2MISS     13
-#define IFLA_VXLAN_L3MISS     14
-#define IFLA_VXLAN_PORT       15
-#define IFLA_VXLAN_GROUP6     16
-#define IFLA_VXLAN_LOCAL6     17
-#undef IFLA_VXLAN_MAX
-#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6
-
-#define IFLA_VRF_TABLE 1
-
-/* older kernel header might not contain 'struct ifla_vxlan_port_range'.
- * Redefine it. */
-struct nm_ifla_vxlan_port_range {
-    guint16 low;
-    guint16 high;
-};
-
-static NMPObject *
-_parse_lnk_vxlan(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_VXLAN_ID]         = {.type = NLA_U32},
-        [IFLA_VXLAN_GROUP]      = {.type = NLA_U32},
-        [IFLA_VXLAN_GROUP6]     = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)},
-        [IFLA_VXLAN_LINK]       = {.type = NLA_U32},
-        [IFLA_VXLAN_LOCAL]      = {.type = NLA_U32},
-        [IFLA_VXLAN_LOCAL6]     = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)},
-        [IFLA_VXLAN_TOS]        = {.type = NLA_U8},
-        [IFLA_VXLAN_TTL]        = {.type = NLA_U8},
-        [IFLA_VXLAN_LEARNING]   = {.type = NLA_U8},
-        [IFLA_VXLAN_AGEING]     = {.type = NLA_U32},
-        [IFLA_VXLAN_LIMIT]      = {.type = NLA_U32},
-        [IFLA_VXLAN_PORT_RANGE] = {.type   = NLA_UNSPEC,
-                                   .minlen = sizeof(struct nm_ifla_vxlan_port_range)},
-        [IFLA_VXLAN_PROXY]      = {.type = NLA_U8},
-        [IFLA_VXLAN_RSC]        = {.type = NLA_U8},
-        [IFLA_VXLAN_L2MISS]     = {.type = NLA_U8},
-        [IFLA_VXLAN_L3MISS]     = {.type = NLA_U8},
-        [IFLA_VXLAN_PORT]       = {.type = NLA_U16},
-    };
-    NMPlatformLnkVxlan *props;
-    struct nlattr *     tb[G_N_ELEMENTS(policy)];
-    NMPObject *         obj;
-
-    if (!info_data || !nm_streq0(kind, "vxlan"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VXLAN, NULL);
-
-    props = &obj->lnk_vxlan;
-
-    if (tb[IFLA_VXLAN_LINK])
-        props->parent_ifindex = nla_get_u32(tb[IFLA_VXLAN_LINK]);
-    if (tb[IFLA_VXLAN_ID])
-        props->id = nla_get_u32(tb[IFLA_VXLAN_ID]);
-    if (tb[IFLA_VXLAN_GROUP])
-        props->group = nla_get_u32(tb[IFLA_VXLAN_GROUP]);
-    if (tb[IFLA_VXLAN_LOCAL])
-        props->local = nla_get_u32(tb[IFLA_VXLAN_LOCAL]);
-    if (tb[IFLA_VXLAN_LOCAL6])
-        props->local6 = *nla_data_as(struct in6_addr, tb[IFLA_VXLAN_LOCAL6]);
-    if (tb[IFLA_VXLAN_GROUP6])
-        props->group6 = *nla_data_as(struct in6_addr, tb[IFLA_VXLAN_GROUP6]);
-
-    if (tb[IFLA_VXLAN_AGEING])
-        props->ageing = nla_get_u32(tb[IFLA_VXLAN_AGEING]);
-    if (tb[IFLA_VXLAN_LIMIT])
-        props->limit = nla_get_u32(tb[IFLA_VXLAN_LIMIT]);
-    if (tb[IFLA_VXLAN_TOS])
-        props->tos = nla_get_u8(tb[IFLA_VXLAN_TOS]);
-    if (tb[IFLA_VXLAN_TTL])
-        props->ttl = nla_get_u8(tb[IFLA_VXLAN_TTL]);
-
-    if (tb[IFLA_VXLAN_PORT])
-        props->dst_port = ntohs(nla_get_u16(tb[IFLA_VXLAN_PORT]));
-
-    if (tb[IFLA_VXLAN_PORT_RANGE]) {
-        struct nm_ifla_vxlan_port_range *range;
-
-        range = nla_data_as(struct nm_ifla_vxlan_port_range, tb[IFLA_VXLAN_PORT_RANGE]);
-        props->src_port_min = ntohs(range->low);
-        props->src_port_max = ntohs(range->high);
-    }
-
-    if (tb[IFLA_VXLAN_LEARNING])
-        props->learning = !!nla_get_u8(tb[IFLA_VXLAN_LEARNING]);
-    if (tb[IFLA_VXLAN_PROXY])
-        props->proxy = !!nla_get_u8(tb[IFLA_VXLAN_PROXY]);
-    if (tb[IFLA_VXLAN_RSC])
-        props->rsc = !!nla_get_u8(tb[IFLA_VXLAN_RSC]);
-    if (tb[IFLA_VXLAN_L2MISS])
-        props->l2miss = !!nla_get_u8(tb[IFLA_VXLAN_L2MISS]);
-    if (tb[IFLA_VXLAN_L3MISS])
-        props->l3miss = !!nla_get_u8(tb[IFLA_VXLAN_L3MISS]);
-
-    return obj;
-}
-
-static NMPObject *
-_parse_lnk_vrf(const char *kind, struct nlattr *info_data)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_VRF_TABLE] = {.type = NLA_U32},
-    };
-    NMPlatformLnkVrf *props;
-    struct nlattr *   tb[G_N_ELEMENTS(policy)];
-    NMPObject *       obj;
-
-    if (!info_data || !nm_streq0(kind, "vrf"))
-        return NULL;
-
-    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
-        return NULL;
-
-    obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VRF, NULL);
-
-    props = &obj->lnk_vrf;
-
-    if (tb[IFLA_VRF_TABLE])
-        props->table = nla_get_u32(tb[IFLA_VRF_TABLE]);
-
-    return obj;
-}
-
-/*****************************************************************************/
-
-static gboolean
-_wireguard_update_from_allowed_ips_nla(NMPWireGuardAllowedIP *allowed_ip, struct nlattr *nlattr)
-{
-    static const struct nla_policy policy[] = {
-        [WGALLOWEDIP_A_FAMILY]    = {.type = NLA_U16},
-        [WGALLOWEDIP_A_IPADDR]    = {.minlen = sizeof(struct in_addr)},
-        [WGALLOWEDIP_A_CIDR_MASK] = {.type = NLA_U8},
-    };
-    struct nlattr *tb[G_N_ELEMENTS(policy)];
-    int            family;
-    int            addr_len;
-
-    if (nla_parse_nested_arr(tb, nlattr, policy) < 0)
-        return FALSE;
-
-    if (!tb[WGALLOWEDIP_A_FAMILY])
-        return FALSE;
-
-    family = nla_get_u16(tb[WGALLOWEDIP_A_FAMILY]);
-    if (family == AF_INET)
-        addr_len = sizeof(in_addr_t);
-    else if (family == AF_INET6)
-        addr_len = sizeof(struct in6_addr);
-    else
-        return FALSE;
-
-    _check_addr_or_return_val(tb, WGALLOWEDIP_A_IPADDR, addr_len, FALSE);
-
-    *allowed_ip = (NMPWireGuardAllowedIP){
-        .family = family,
-    };
-
-    nm_assert((int) allowed_ip->family == family);
-
-    if (tb[WGALLOWEDIP_A_IPADDR])
-        nla_memcpy(&allowed_ip->addr, tb[WGALLOWEDIP_A_IPADDR], addr_len);
-    if (tb[WGALLOWEDIP_A_CIDR_MASK])
-        allowed_ip->mask = nla_get_u8(tb[WGALLOWEDIP_A_CIDR_MASK]);
-
-    return TRUE;
-}
-
-typedef struct {
-    CList            lst;
-    NMPWireGuardPeer data;
-} WireGuardPeerConstruct;
-
-static gboolean
-_wireguard_update_from_peers_nla(CList *peers, GArray **p_allowed_ips, struct nlattr *peer_attr)
-{
-    static const struct nla_policy policy[] = {
-        [WGPEER_A_PUBLIC_KEY]                    = {.minlen = NMP_WIREGUARD_PUBLIC_KEY_LEN},
-        [WGPEER_A_PRESHARED_KEY]                 = {},
-        [WGPEER_A_FLAGS]                         = {.type = NLA_U32},
-        [WGPEER_A_ENDPOINT]                      = {},
-        [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = {.type = NLA_U16},
-        [WGPEER_A_LAST_HANDSHAKE_TIME]           = {},
-        [WGPEER_A_RX_BYTES]                      = {.type = NLA_U64},
-        [WGPEER_A_TX_BYTES]                      = {.type = NLA_U64},
-        [WGPEER_A_ALLOWEDIPS]                    = {.type = NLA_NESTED},
-    };
-    struct nlattr *         tb[G_N_ELEMENTS(policy)];
-    WireGuardPeerConstruct *peer_c;
-
-    if (nla_parse_nested_arr(tb, peer_attr, policy) < 0)
-        return FALSE;
-
-    if (!tb[WGPEER_A_PUBLIC_KEY])
-        return FALSE;
-
-    /* a peer with the same public key as last peer is just a continuation for extra AllowedIPs */
-    peer_c = c_list_last_entry(peers, WireGuardPeerConstruct, lst);
-    if (peer_c
-        && !memcmp(nla_data(tb[WGPEER_A_PUBLIC_KEY]),
-                   peer_c->data.public_key,
-                   NMP_WIREGUARD_PUBLIC_KEY_LEN)) {
-        G_STATIC_ASSERT_EXPR(NMP_WIREGUARD_PUBLIC_KEY_LEN == sizeof(peer_c->data.public_key));
-        /* this message is a continuation of the previous peer.
-         * Only parse WGPEER_A_ALLOWEDIPS below. */
-    } else {
-        /* otherwise, start a new peer */
-        peer_c = g_slice_new0(WireGuardPeerConstruct);
-        c_list_link_tail(peers, &peer_c->lst);
-
-        nla_memcpy(&peer_c->data.public_key,
-                   tb[WGPEER_A_PUBLIC_KEY],
-                   sizeof(peer_c->data.public_key));
-
-        if (tb[WGPEER_A_PRESHARED_KEY]) {
-            nla_memcpy(&peer_c->data.preshared_key,
-                       tb[WGPEER_A_PRESHARED_KEY],
-                       sizeof(peer_c->data.preshared_key));
-            /* FIXME(netlink-bzero-secret) */
-            nm_explicit_bzero(nla_data(tb[WGPEER_A_PRESHARED_KEY]),
-                              nla_len(tb[WGPEER_A_PRESHARED_KEY]));
-        }
-
-        nm_sock_addr_union_cpy_untrusted(
-            &peer_c->data.endpoint,
-            tb[WGPEER_A_ENDPOINT] ? nla_data(tb[WGPEER_A_ENDPOINT]) : NULL,
-            tb[WGPEER_A_ENDPOINT] ? nla_len(tb[WGPEER_A_ENDPOINT]) : 0);
-
-        if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL])
-            peer_c->data.persistent_keepalive_interval =
-                nla_get_u16(tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]);
-        if (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) {
-            if (nla_len(tb[WGPEER_A_LAST_HANDSHAKE_TIME])
-                >= sizeof(peer_c->data.last_handshake_time))
-                nla_memcpy(&peer_c->data.last_handshake_time,
-                           tb[WGPEER_A_LAST_HANDSHAKE_TIME],
-                           sizeof(peer_c->data.last_handshake_time));
-        }
-        if (tb[WGPEER_A_RX_BYTES])
-            peer_c->data.rx_bytes = nla_get_u64(tb[WGPEER_A_RX_BYTES]);
-        if (tb[WGPEER_A_TX_BYTES])
-            peer_c->data.tx_bytes = nla_get_u64(tb[WGPEER_A_TX_BYTES]);
-    }
-
-    if (tb[WGPEER_A_ALLOWEDIPS]) {
-        struct nlattr *attr;
-        int            rem;
-        GArray *       allowed_ips = *p_allowed_ips;
-
-        nla_for_each_nested (attr, tb[WGPEER_A_ALLOWEDIPS], rem) {
-            if (!allowed_ips) {
-                allowed_ips    = g_array_new(FALSE, FALSE, sizeof(NMPWireGuardAllowedIP));
-                *p_allowed_ips = allowed_ips;
-                g_array_set_size(allowed_ips, 1);
-            } else
-                g_array_set_size(allowed_ips, allowed_ips->len + 1);
-
-            if (!_wireguard_update_from_allowed_ips_nla(
-                    &g_array_index(allowed_ips, NMPWireGuardAllowedIP, allowed_ips->len - 1),
-                    attr)) {
-                /* we ignore the error of parsing one allowed-ip. */
-                g_array_set_size(allowed_ips, allowed_ips->len - 1);
-                continue;
-            }
-
-            if (!peer_c->data._construct_idx_end)
-                peer_c->data._construct_idx_start = allowed_ips->len - 1;
-            peer_c->data._construct_idx_end = allowed_ips->len;
-        }
-    }
-
-    return TRUE;
-}
-
-typedef struct {
-    const int  ifindex;
-    NMPObject *obj;
-    CList      peers;
-    GArray *   allowed_ips;
-} WireGuardParseData;
-
-static int
-_wireguard_get_device_cb(struct nl_msg *msg, void *arg)
-{
-    static const struct nla_policy policy[] = {
-        [WGDEVICE_A_IFINDEX]     = {.type = NLA_U32},
-        [WGDEVICE_A_IFNAME]      = {.type = NLA_NUL_STRING, .maxlen = IFNAMSIZ},
-        [WGDEVICE_A_PRIVATE_KEY] = {},
-        [WGDEVICE_A_PUBLIC_KEY]  = {},
-        [WGDEVICE_A_FLAGS]       = {.type = NLA_U32},
-        [WGDEVICE_A_LISTEN_PORT] = {.type = NLA_U16},
-        [WGDEVICE_A_FWMARK]      = {.type = NLA_U32},
-        [WGDEVICE_A_PEERS]       = {.type = NLA_NESTED},
-    };
-    struct nlattr *     tb[G_N_ELEMENTS(policy)];
-    WireGuardParseData *parse_data = arg;
-
-    if (genlmsg_parse_arr(nlmsg_hdr(msg), 0, tb, policy) < 0)
-        return NL_SKIP;
-
-    if (tb[WGDEVICE_A_IFINDEX]) {
-        int ifindex;
-
-        ifindex = (int) nla_get_u32(tb[WGDEVICE_A_IFINDEX]);
-        if (ifindex <= 0 || parse_data->ifindex != ifindex)
-            return NL_SKIP;
-    } else {
-        if (!parse_data->obj)
-            return NL_SKIP;
-    }
-
-    if (parse_data->obj) {
-        /* we already have an object instance. This means the netlink message
-         * is a continuation, only providing more WGDEVICE_A_PEERS data below. */
-    } else {
-        NMPObject *             obj;
-        NMPlatformLnkWireGuard *props;
-
-        obj   = nmp_object_new(NMP_OBJECT_TYPE_LNK_WIREGUARD, NULL);
-        props = &obj->lnk_wireguard;
-
-        if (tb[WGDEVICE_A_PRIVATE_KEY]) {
-            nla_memcpy(props->private_key, tb[WGDEVICE_A_PRIVATE_KEY], sizeof(props->private_key));
-            /* FIXME(netlink-bzero-secret): extend netlink library to wipe memory. For now,
-             * just hack it here (yes, this does not cover all places where the
-             * private key was copied). */
-            nm_explicit_bzero(nla_data(tb[WGDEVICE_A_PRIVATE_KEY]),
-                              nla_len(tb[WGDEVICE_A_PRIVATE_KEY]));
-        }
-        if (tb[WGDEVICE_A_PUBLIC_KEY])
-            nla_memcpy(props->public_key, tb[WGDEVICE_A_PUBLIC_KEY], sizeof(props->public_key));
-        if (tb[WGDEVICE_A_LISTEN_PORT])
-            props->listen_port = nla_get_u16(tb[WGDEVICE_A_LISTEN_PORT]);
-        if (tb[WGDEVICE_A_FWMARK])
-            props->fwmark = nla_get_u32(tb[WGDEVICE_A_FWMARK]);
-
-        parse_data->obj = obj;
-    }
-
-    if (tb[WGDEVICE_A_PEERS]) {
-        struct nlattr *attr;
-        int            rem;
-
-        nla_for_each_nested (attr, tb[WGDEVICE_A_PEERS], rem) {
-            if (!_wireguard_update_from_peers_nla(&parse_data->peers,
-                                                  &parse_data->allowed_ips,
-                                                  attr)) {
-                /* we ignore the error of parsing one peer.
-                 * _wireguard_update_from_peers_nla() leaves the @peers array in the
-                 * desired state. */
-            }
-        }
-    }
-
-    return NL_OK;
-}
-
-static const NMPObject *
-_wireguard_read_info(NMPlatform *    platform /* used only as logging context */,
-                     struct nl_sock *genl,
-                     int             wireguard_family_id,
-                     int             ifindex)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    NMPObject *                  obj = NULL;
-    WireGuardPeerConstruct *     peer_c;
-    WireGuardPeerConstruct *     peer_c_safe;
-    gs_unref_array GArray *allowed_ips = NULL;
-    WireGuardParseData     parse_data  = {
-        .ifindex = ifindex,
-    };
-    guint i;
-
-    nm_assert(genl);
-    nm_assert(wireguard_family_id >= 0);
-    nm_assert(ifindex > 0);
-
-    _LOGT("wireguard: fetching information for ifindex %d (genl-id %d)...",
-          ifindex,
-          wireguard_family_id);
-
-    msg = nlmsg_alloc();
-
-    if (!genlmsg_put(msg,
-                     NL_AUTO_PORT,
-                     NL_AUTO_SEQ,
-                     wireguard_family_id,
-                     0,
-                     NLM_F_DUMP,
-                     WG_CMD_GET_DEVICE,
-                     1))
-        return NULL;
-
-    NLA_PUT_U32(msg, WGDEVICE_A_IFINDEX, (guint32) ifindex);
-
-    if (nl_send_auto(genl, msg) < 0)
-        return NULL;
-
-    c_list_init(&parse_data.peers);
-
-    /* we ignore errors, and return whatever we could successfully
-     * parse. */
-    nl_recvmsgs(genl,
-                &((const struct nl_cb){
-                    .valid_cb  = _wireguard_get_device_cb,
-                    .valid_arg = (gpointer) &parse_data,
-                }));
-
-    /* unpack: transfer ownership */
-    obj         = parse_data.obj;
-    allowed_ips = parse_data.allowed_ips;
-
-    if (!obj) {
-        while ((peer_c = c_list_first_entry(&parse_data.peers, WireGuardPeerConstruct, lst))) {
-            c_list_unlink_stale(&peer_c->lst);
-            nm_explicit_bzero(&peer_c->data.preshared_key, sizeof(peer_c->data.preshared_key));
-            g_slice_free(WireGuardPeerConstruct, peer_c);
-        }
-        return NULL;
-    }
-
-    /* we receive peers/allowed-ips possibly in separate netlink messages. Hence, while
-     * parsing the dump, we don't know upfront how many peers/allowed-ips we will receive.
-     *
-     * We solve that, by collecting all peers with a CList. It's done this way,
-     * because a GArray would require growing the array, but we want to bzero()
-     * the preshared-key of each peer while reallocating. The CList apprach avoids
-     * that.
-     *
-     * For allowed-ips, we instead track one GArray, which are all appended
-     * there. The realloc/resize of the GArray is fine there. However,
-     * while we build the GArray, we don't yet have the final pointers.
-     * Hence, while constructing, we track the indexes with peer->_construct_idx_*
-     * fields. These indexes must be converted to actual pointers blow.
-     *
-     * This is all done during parsing. In the final NMPObjectLnkWireGuard we
-     * don't want the CList anymore and repackage the NMPObject tightly. The
-     * reason is, that NMPObject instances are immutable and long-living. Spend
-     * a bit effort below during construction to obtain a most suitable representation
-     * in this regard. */
-    obj->_lnk_wireguard.peers_len = c_list_length(&parse_data.peers);
-    obj->_lnk_wireguard.peers     = obj->_lnk_wireguard.peers_len > 0
-                                        ? g_new(NMPWireGuardPeer, obj->_lnk_wireguard.peers_len)
-                                        : NULL;
-
-    /* duplicate allowed_ips instead of using the pointer. The GArray possibly has more
-     * space allocated then we need, and we want to get rid of this excess buffer.
-     * Note that NMPObject instance is possibly put into the cache and long-living. */
-    obj->_lnk_wireguard._allowed_ips_buf_len = allowed_ips ? allowed_ips->len : 0u;
-    obj->_lnk_wireguard._allowed_ips_buf =
-        obj->_lnk_wireguard._allowed_ips_buf_len > 0
-            ? (NMPWireGuardAllowedIP *) nm_memdup(allowed_ips->data,
-                                                  sizeof(NMPWireGuardAllowedIP) * allowed_ips->len)
-            : NULL;
-
-    i = 0;
-    c_list_for_each_entry_safe (peer_c, peer_c_safe, &parse_data.peers, lst) {
-        NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &obj->_lnk_wireguard.peers[i++];
-
-        *peer = peer_c->data;
-
-        c_list_unlink_stale(&peer_c->lst);
-        nm_explicit_bzero(&peer_c->data.preshared_key, sizeof(peer_c->data.preshared_key));
-        g_slice_free(WireGuardPeerConstruct, peer_c);
-
-        if (peer->_construct_idx_end != 0) {
-            guint len;
-
-            nm_assert(obj->_lnk_wireguard._allowed_ips_buf);
-            nm_assert(peer->_construct_idx_end > peer->_construct_idx_start);
-            nm_assert(peer->_construct_idx_start < obj->_lnk_wireguard._allowed_ips_buf_len);
-            nm_assert(peer->_construct_idx_end <= obj->_lnk_wireguard._allowed_ips_buf_len);
-
-            len               = peer->_construct_idx_end - peer->_construct_idx_start;
-            peer->allowed_ips = &obj->_lnk_wireguard._allowed_ips_buf[peer->_construct_idx_start];
-            peer->allowed_ips_len = len;
-        } else {
-            nm_assert(!peer->_construct_idx_start);
-            nm_assert(!peer->_construct_idx_end);
-            peer->allowed_ips     = NULL;
-            peer->allowed_ips_len = 0;
-        }
-    }
-
-    return obj;
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static int
-_wireguard_get_family_id(NMPlatform *platform, int ifindex_try)
-{
-    NMLinuxPlatformPrivate *priv                = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    int                     wireguard_family_id = -1;
-
-    if (ifindex_try > 0) {
-        const NMPlatformLink *plink;
-
-        if (nm_platform_link_get_lnk_wireguard(platform, ifindex_try, &plink))
-            wireguard_family_id = NMP_OBJECT_UP_CAST(plink)->_link.wireguard_family_id;
-    }
-    if (wireguard_family_id < 0)
-        wireguard_family_id = genl_ctrl_resolve(priv->genl, "wireguard");
-    return wireguard_family_id;
-}
-
-static const NMPObject *
-_wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifindex)
-{
-    NMLinuxPlatformPrivate *priv            = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    nm_auto_nmpobj const NMPObject *obj_old = NULL;
-    nm_auto_nmpobj const NMPObject *obj_new = NULL;
-    nm_auto_nmpobj const NMPObject *lnk_new = NULL;
-    NMPCacheOpsType                 cache_op;
-    const NMPObject *               plink = NULL;
-    nm_auto_nmpobj NMPObject *obj         = NULL;
-
-    nm_assert(wireguard_family_id >= 0);
-    nm_assert(ifindex > 0);
-
-    nm_platform_process_events(platform);
-
-    plink = nm_platform_link_get_obj(platform, ifindex, TRUE);
-
-    if (!plink || plink->link.type != NM_LINK_TYPE_WIREGUARD) {
-        nm_platform_link_refresh(platform, ifindex);
-        plink = nm_platform_link_get_obj(platform, ifindex, TRUE);
-        if (!plink || plink->link.type != NM_LINK_TYPE_WIREGUARD)
-            return NULL;
-        if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD)
-            lnk_new = nmp_object_ref(plink->_link.netlink.lnk);
-    } else {
-        lnk_new = _wireguard_read_info(platform, priv->genl, wireguard_family_id, ifindex);
-        if (!lnk_new) {
-            if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD)
-                lnk_new = nmp_object_ref(plink->_link.netlink.lnk);
-        } else if (nmp_object_equal(plink->_link.netlink.lnk, lnk_new)) {
-            nmp_object_unref(lnk_new);
-            lnk_new = nmp_object_ref(plink->_link.netlink.lnk);
-        }
-    }
-
-    if (plink->_link.wireguard_family_id == wireguard_family_id
-        && plink->_link.netlink.lnk == lnk_new)
-        return plink;
-
-    /* we use nmp_cache_update_netlink() to re-inject the new object into the cache.
-     * For that, we need to clone it, and tweak it so that it's suitable. It's a bit
-     * of a hack, in particular that we need to clear driver and udev-device. */
-    obj                            = nmp_object_clone(plink, FALSE);
-    obj->_link.wireguard_family_id = wireguard_family_id;
-    nmp_object_unref(obj->_link.netlink.lnk);
-    obj->_link.netlink.lnk = g_steal_pointer(&lnk_new);
-    obj->link.driver       = NULL;
-    nm_clear_pointer(&obj->_link.udev.device, udev_device_unref);
-
-    cache_op =
-        nmp_cache_update_netlink(nm_platform_get_cache(platform), obj, FALSE, &obj_old, &obj_new);
-    nm_assert(NM_IN_SET(cache_op, NMP_CACHE_OPS_UPDATED));
-    if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
-        cache_on_change(platform, cache_op, obj_old, obj_new);
-        nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new);
-    }
-
-    nm_assert(!obj_new
-              || (NMP_OBJECT_GET_TYPE(obj_new) == NMP_OBJECT_TYPE_LINK
-                  && obj_new->link.type == NM_LINK_TYPE_WIREGUARD
-                  && (!obj_new->_link.netlink.lnk
-                      || NMP_OBJECT_GET_TYPE(obj_new->_link.netlink.lnk)
-                             == NMP_OBJECT_TYPE_LNK_WIREGUARD)));
-    return obj_new;
-}
-
-static int
-_wireguard_create_change_nlmsgs(NMPlatform *                              platform,
-                                int                                       ifindex,
-                                int                                       wireguard_family_id,
-                                const NMPlatformLnkWireGuard *            lnk_wireguard,
-                                const NMPWireGuardPeer *                  peers,
-                                const NMPlatformWireGuardChangePeerFlags *peer_flags,
-                                guint                                     peers_len,
-                                NMPlatformWireGuardChangeFlags            change_flags,
-                                GPtrArray **                              out_msgs)
-{
-    gs_unref_ptrarray GPtrArray *      msgs    = NULL;
-    nm_auto_nlmsg struct nl_msg *      msg     = NULL;
-    const guint                        IDX_NIL = G_MAXUINT;
-    guint                              idx_peer_curr;
-    guint                              idx_allowed_ips_curr;
-    struct nlattr *                    nest_peers;
-    struct nlattr *                    nest_curr_peer;
-    struct nlattr *                    nest_allowed_ips;
-    struct nlattr *                    nest_curr_allowed_ip;
-    NMPlatformWireGuardChangePeerFlags p_flags = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT;
-
-#define _nla_nest_end(msg, nest_start)             \
-    G_STMT_START                                   \
-    {                                              \
-        if (nla_nest_end((msg), (nest_start)) < 0) \
-            g_return_val_if_reached(-NME_BUG);     \
-    }                                              \
-    G_STMT_END
-
-    /* Adapted from LGPL-2.1+ code [1].
-     *
-     * [1] https://git.zx2c4.com/WireGuard/tree/contrib/examples/embeddable-wg-library/wireguard.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n1073 */
-
-    idx_peer_curr        = IDX_NIL;
-    idx_allowed_ips_curr = IDX_NIL;
-
-    /* TODO: for the moment, we always reset all peers and allowed-ips (WGDEVICE_F_REPLACE_PEERS, WGPEER_F_REPLACE_ALLOWEDIPS).
-     * The platform API should be extended to also support partial updates. In particular, configuring the same configuration
-     * multiple times, should not clear and re-add all settings, but rather sync the existing settings with the desired configuration. */
-
-again:
-
-    msg = nlmsg_alloc();
-    if (!genlmsg_put(msg,
-                     NL_AUTO_PORT,
-                     NL_AUTO_SEQ,
-                     wireguard_family_id,
-                     0,
-                     NLM_F_REQUEST,
-                     WG_CMD_SET_DEVICE,
-                     1))
-        g_return_val_if_reached(-NME_BUG);
-
-    NLA_PUT_U32(msg, WGDEVICE_A_IFINDEX, (guint32) ifindex);
-
-    if (idx_peer_curr == IDX_NIL) {
-        guint32 flags;
-
-        if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY))
-            NLA_PUT(msg,
-                    WGDEVICE_A_PRIVATE_KEY,
-                    sizeof(lnk_wireguard->private_key),
-                    lnk_wireguard->private_key);
-        if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT))
-            NLA_PUT_U16(msg, WGDEVICE_A_LISTEN_PORT, lnk_wireguard->listen_port);
-        if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK))
-            NLA_PUT_U32(msg, WGDEVICE_A_FWMARK, lnk_wireguard->fwmark);
-
-        flags = 0;
-        if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS))
-            flags |= WGDEVICE_F_REPLACE_PEERS;
-        NLA_PUT_U32(msg, WGDEVICE_A_FLAGS, flags);
-    }
-
-    if (peers_len == 0)
-        goto send;
-
-    nest_curr_peer       = NULL;
-    nest_allowed_ips     = NULL;
-    nest_curr_allowed_ip = NULL;
-
-    nest_peers = nla_nest_start(msg, WGDEVICE_A_PEERS);
-    if (!nest_peers)
-        g_return_val_if_reached(-NME_BUG);
-
-    if (idx_peer_curr == IDX_NIL)
-        idx_peer_curr = 0;
-    for (; idx_peer_curr < peers_len; idx_peer_curr++) {
-        const NMPWireGuardPeer *p = &peers[idx_peer_curr];
-
-        if (peer_flags) {
-            p_flags = peer_flags[idx_peer_curr];
-            if (!NM_FLAGS_ANY(p_flags,
-                              NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME
-                                  | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY
-                                  | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL
-                                  | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT
-                                  | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS
-                                  | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS)) {
-                /* no flags set. We take that as indication to skip configuring the peer
-                 * entirely. */
-                nm_assert(p_flags == NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE);
-                continue;
-            }
-        }
-
-        nest_curr_peer = nla_nest_start(msg, 0);
-        if (!nest_curr_peer)
-            goto toobig_peers;
-
-        if (nla_put(msg, WGPEER_A_PUBLIC_KEY, NMP_WIREGUARD_PUBLIC_KEY_LEN, p->public_key) < 0)
-            goto toobig_peers;
-
-        if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME)) {
-            /* all other p_flags are silently ignored. */
-            if (nla_put_uint32(msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME) < 0)
-                goto toobig_peers;
-        } else {
-            if (idx_allowed_ips_curr == IDX_NIL) {
-                if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY)
-                    && nla_put(msg,
-                               WGPEER_A_PRESHARED_KEY,
-                               sizeof(p->preshared_key),
-                               p->preshared_key)
-                           < 0)
-                    goto toobig_peers;
-
-                if (NM_FLAGS_HAS(p_flags,
-                                 NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL)
-                    && nla_put_uint16(msg,
-                                      WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
-                                      p->persistent_keepalive_interval)
-                           < 0)
-                    goto toobig_peers;
-
-                if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS)
-                    && nla_put_uint32(msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS) < 0)
-                    goto toobig_peers;
-
-                if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT)) {
-                    if (NM_IN_SET(p->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
-                        if (nla_put(msg,
-                                    WGPEER_A_ENDPOINT,
-                                    p->endpoint.sa.sa_family == AF_INET ? sizeof(p->endpoint.in)
-                                                                        : sizeof(p->endpoint.in6),
-                                    &p->endpoint)
-                            < 0)
-                            goto toobig_peers;
-                    } else {
-                        /* I think there is no way to clear an endpoint, though there should be. */
-                        nm_assert(p->endpoint.sa.sa_family == AF_UNSPEC);
-                    }
-                }
-            }
-
-            if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS)
-                && p->allowed_ips_len > 0) {
-                if (idx_allowed_ips_curr == IDX_NIL)
-                    idx_allowed_ips_curr = 0;
-
-                nest_allowed_ips = nla_nest_start(msg, WGPEER_A_ALLOWEDIPS);
-                if (!nest_allowed_ips)
-                    goto toobig_allowedips;
-
-                for (; idx_allowed_ips_curr < p->allowed_ips_len; idx_allowed_ips_curr++) {
-                    const NMPWireGuardAllowedIP *aip = &p->allowed_ips[idx_allowed_ips_curr];
-
-                    nest_curr_allowed_ip = nla_nest_start(msg, 0);
-                    if (!nest_curr_allowed_ip)
-                        goto toobig_allowedips;
-
-                    g_return_val_if_fail(NM_IN_SET(aip->family, AF_INET, AF_INET6), -NME_BUG);
-
-                    if (nla_put_uint16(msg, WGALLOWEDIP_A_FAMILY, aip->family) < 0)
-                        goto toobig_allowedips;
-                    if (nla_put(msg,
-                                WGALLOWEDIP_A_IPADDR,
-                                nm_utils_addr_family_to_size(aip->family),
-                                &aip->addr)
-                        < 0)
-                        goto toobig_allowedips;
-                    if (nla_put_uint8(msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0)
-                        goto toobig_allowedips;
-
-                    _nla_nest_end(msg, nest_curr_allowed_ip);
-                    nest_curr_allowed_ip = NULL;
-                }
-                idx_allowed_ips_curr = IDX_NIL;
-
-                _nla_nest_end(msg, nest_allowed_ips);
-                nest_allowed_ips = NULL;
-            }
-        }
-
-        _nla_nest_end(msg, nest_curr_peer);
-        nest_curr_peer = NULL;
-    }
-
-    _nla_nest_end(msg, nest_peers);
-    goto send;
-
-toobig_allowedips:
-    if (nest_curr_allowed_ip)
-        nla_nest_cancel(msg, nest_curr_allowed_ip);
-    if (nest_allowed_ips)
-        _nla_nest_end(msg, nest_allowed_ips);
-    _nla_nest_end(msg, nest_curr_peer);
-    _nla_nest_end(msg, nest_peers);
-    goto send;
-
-toobig_peers:
-    if (nest_curr_peer)
-        nla_nest_cancel(msg, nest_curr_peer);
-    _nla_nest_end(msg, nest_peers);
-    goto send;
-
-send:
-    if (!msgs)
-        msgs = g_ptr_array_new_with_free_func((GDestroyNotify) nlmsg_free);
-    g_ptr_array_add(msgs, g_steal_pointer(&msg));
-
-    if (idx_peer_curr != IDX_NIL && idx_peer_curr < peers_len)
-        goto again;
-
-    NM_SET_OUT(out_msgs, g_steal_pointer(&msgs));
-    return 0;
-
-nla_put_failure:
-    g_return_val_if_reached(-NME_BUG);
-
-#undef _nla_nest_end
-}
-
-static int
-link_wireguard_change(NMPlatform *                              platform,
-                      int                                       ifindex,
-                      const NMPlatformLnkWireGuard *            lnk_wireguard,
-                      const NMPWireGuardPeer *                  peers,
-                      const NMPlatformWireGuardChangePeerFlags *peer_flags,
-                      guint                                     peers_len,
-                      NMPlatformWireGuardChangeFlags            change_flags)
-{
-    NMLinuxPlatformPrivate *priv      = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    gs_unref_ptrarray GPtrArray *msgs = NULL;
-    int                          wireguard_family_id;
-    guint                        i;
-    int                          r;
-
-    wireguard_family_id = _wireguard_get_family_id(platform, ifindex);
-    if (wireguard_family_id < 0)
-        return -NME_PL_NO_FIRMWARE;
-
-    r = _wireguard_create_change_nlmsgs(platform,
-                                        ifindex,
-                                        wireguard_family_id,
-                                        lnk_wireguard,
-                                        peers,
-                                        peer_flags,
-                                        peers_len,
-                                        change_flags,
-                                        &msgs);
-    if (r < 0) {
-        _LOGW("wireguard: set-device, cannot construct netlink message: %s", nm_strerror(r));
-        return r;
-    }
-
-    for (i = 0; i < msgs->len; i++) {
-        r = nl_send_auto(priv->genl, msgs->pdata[i]);
-        if (r < 0) {
-            _LOGW("wireguard: set-device, send netlink message #%u failed: %s", i, nm_strerror(r));
-            return r;
-        }
-
-        do {
-            r = nl_recvmsgs(priv->genl, NULL);
-        } while (r == -EAGAIN);
-        if (r < 0) {
-            _LOGW("wireguard: set-device, message #%u was rejected: %s", i, nm_strerror(r));
-            return r;
-        }
-
-        _LOGT("wireguard: set-device, message #%u sent and confirmed", i);
-    }
-
-    _wireguard_refresh_link(platform, wireguard_family_id, ifindex);
-
-    return 0;
-}
-
-/*****************************************************************************/
-
-static void
-_nmp_link_address_set(NMPLinkAddress *dst, const struct nlattr *nla)
-{
-    *dst = (NMPLinkAddress){
-        .len = 0,
-    };
-    if (nla) {
-        int l = nla_len(nla);
-
-        if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) {
-            G_STATIC_ASSERT_EXPR(sizeof(dst->data) == NM_UTILS_HWADDR_LEN_MAX);
-            memcpy(dst->data, nla_data(nla), l);
-            dst->len = l;
-        }
-    }
-}
-
-/* Copied and heavily modified from libnl3's link_msg_parser(). */
-static NMPObject *
-_new_from_nl_link(NMPlatform *     platform,
-                  const NMPCache * cache,
-                  struct nlmsghdr *nlh,
-                  gboolean         id_only)
-{
-    static const struct nla_policy policy[] = {
-        [IFLA_IFNAME]        = {.type = NLA_STRING, .maxlen = IFNAMSIZ},
-        [IFLA_MTU]           = {.type = NLA_U32},
-        [IFLA_TXQLEN]        = {.type = NLA_U32},
-        [IFLA_LINK]          = {.type = NLA_U32},
-        [IFLA_WEIGHT]        = {.type = NLA_U32},
-        [IFLA_MASTER]        = {.type = NLA_U32},
-        [IFLA_OPERSTATE]     = {.type = NLA_U8},
-        [IFLA_LINKMODE]      = {.type = NLA_U8},
-        [IFLA_LINKINFO]      = {.type = NLA_NESTED},
-        [IFLA_QDISC]         = {.type = NLA_STRING, .maxlen = IFQDISCSIZ},
-        [IFLA_STATS]         = {.minlen = nm_offsetofend(struct rtnl_link_stats, tx_compressed)},
-        [IFLA_STATS64]       = {.minlen = nm_offsetofend(struct rtnl_link_stats64, tx_compressed)},
-        [IFLA_MAP]           = {.minlen = nm_offsetofend(struct rtnl_link_ifmap, port)},
-        [IFLA_IFALIAS]       = {.type = NLA_STRING, .maxlen = IFALIASZ},
-        [IFLA_NUM_VF]        = {.type = NLA_U32},
-        [IFLA_AF_SPEC]       = {.type = NLA_NESTED},
-        [IFLA_PROMISCUITY]   = {.type = NLA_U32},
-        [IFLA_NUM_TX_QUEUES] = {.type = NLA_U32},
-        [IFLA_NUM_RX_QUEUES] = {.type = NLA_U32},
-        [IFLA_GROUP]         = {.type = NLA_U32},
-        [IFLA_CARRIER]       = {.type = NLA_U8},
-        [IFLA_PHYS_PORT_ID]  = {.type = NLA_UNSPEC},
-        [IFLA_NET_NS_PID]    = {.type = NLA_U32},
-        [IFLA_NET_NS_FD]     = {.type = NLA_U32},
-        [IFLA_LINK_NETNSID]  = {},
-    };
-    const struct ifinfomsg *ifi;
-    struct nlattr *         tb[G_N_ELEMENTS(policy)];
-    struct nlattr *         nl_info_data               = NULL;
-    const char *            nl_info_kind               = NULL;
-    nm_auto_nmpobj NMPObject *obj                      = NULL;
-    gboolean                  completed_from_cache_val = FALSE;
-    gboolean *                completed_from_cache     = cache ? &completed_from_cache_val : NULL;
-    const NMPObject *         link_cached              = NULL;
-    const NMPObject *         lnk_data                 = NULL;
-    gboolean                  address_complete_from_cache   = TRUE;
-    gboolean                  broadcast_complete_from_cache = TRUE;
-    gboolean                  lnk_data_complete_from_cache  = TRUE;
-    gboolean                  need_ext_data                 = FALSE;
-    gboolean                  af_inet6_token_valid          = FALSE;
-    gboolean                  af_inet6_addr_gen_mode_valid  = FALSE;
-
-    if (!nlmsg_valid_hdr(nlh, sizeof(*ifi)))
-        return NULL;
-
-    ifi = nlmsg_data(nlh);
-
-    if (ifi->ifi_family != AF_UNSPEC)
-        return NULL;
-    if (ifi->ifi_index <= 0)
-        return NULL;
-
-    obj = nmp_object_new_link(ifi->ifi_index);
-
-    if (id_only)
-        return g_steal_pointer(&obj);
-
-    if (nlmsg_parse_arr(nlh, sizeof(*ifi), tb, policy) < 0)
-        return NULL;
-
-    if (!tb[IFLA_IFNAME])
-        return NULL;
-    nla_strlcpy(obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ);
-    if (!obj->link.name[0])
-        return NULL;
-
-    if (!tb[IFLA_MTU]) {
-        /* Kernel has two places that send RTM_GETLINK messages:
-         * net/core/rtnetlink.c and net/wireless/ext-core.c.
-         * Unfortunately ext-core.c sets only IFLA_WIRELESS and
-         * IFLA_IFNAME. This confuses code in this function, because
-         * it cannot get complete set of data for the interface and
-         * later incomplete object this function creates is used to
-         * overwrite existing data in NM's cache.
-         * Since ext-core.c doesn't set IFLA_MTU we can use it as a
-         * signal to ignore incoming message.
-         * To some extent this is a hack and correct approach is to
-         * merge objects per-field.
-         */
-        return NULL;
-    }
-    obj->link.mtu = nla_get_u32(tb[IFLA_MTU]);
-
-    if (tb[IFLA_LINKINFO]) {
-        static const struct nla_policy policy_link_info[] = {
-            [IFLA_INFO_KIND]   = {.type = NLA_STRING},
-            [IFLA_INFO_DATA]   = {.type = NLA_NESTED},
-            [IFLA_INFO_XSTATS] = {.type = NLA_NESTED},
-        };
-        struct nlattr *li[G_N_ELEMENTS(policy_link_info)];
-
-        if (nla_parse_nested_arr(li, tb[IFLA_LINKINFO], policy_link_info) < 0)
-            return NULL;
-
-        if (li[IFLA_INFO_KIND])
-            nl_info_kind = nla_get_string(li[IFLA_INFO_KIND]);
-
-        nl_info_data = li[IFLA_INFO_DATA];
-    }
-
-    if (tb[IFLA_STATS64]) {
-        const char *stats = nla_data(tb[IFLA_STATS64]);
-
-        obj->link.rx_packets =
-            unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, rx_packets)]);
-        obj->link.rx_bytes =
-            unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, rx_bytes)]);
-        obj->link.tx_packets =
-            unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, tx_packets)]);
-        obj->link.tx_bytes =
-            unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, tx_bytes)]);
-    }
-
-    obj->link.n_ifi_flags = ifi->ifi_flags;
-    obj->link.connected   = NM_FLAGS_HAS(obj->link.n_ifi_flags, IFF_LOWER_UP);
-    obj->link.arptype     = ifi->ifi_type;
-
-    obj->link.type = _linktype_get_type(platform,
-                                        cache,
-                                        nl_info_kind,
-                                        obj->link.ifindex,
-                                        obj->link.name,
-                                        obj->link.n_ifi_flags,
-                                        obj->link.arptype,
-                                        completed_from_cache,
-                                        &link_cached,
-                                        &obj->link.kind);
-
-    if (tb[IFLA_MASTER])
-        obj->link.master = nla_get_u32(tb[IFLA_MASTER]);
-
-    if (tb[IFLA_LINK]) {
-        if (!tb[IFLA_LINK_NETNSID])
-            obj->link.parent = nla_get_u32(tb[IFLA_LINK]);
-        else
-            obj->link.parent = NM_PLATFORM_LINK_OTHER_NETNS;
-    }
-
-    if (tb[IFLA_ADDRESS]) {
-        _nmp_link_address_set(&obj->link.l_address, tb[IFLA_ADDRESS]);
-        address_complete_from_cache = FALSE;
-    }
-
-    if (tb[IFLA_BROADCAST]) {
-        _nmp_link_address_set(&obj->link.l_broadcast, tb[IFLA_BROADCAST]);
-        broadcast_complete_from_cache = FALSE;
-    }
-
-    if (tb[IFLA_AF_SPEC]) {
-        struct nlattr *af_attr;
-        int            remaining;
-
-        nla_for_each_nested (af_attr, tb[IFLA_AF_SPEC], remaining) {
-            switch (nla_type(af_attr)) {
-            case AF_INET6:
-                _parse_af_inet6(platform,
-                                af_attr,
-                                &obj->link.inet6_token,
-                                &af_inet6_token_valid,
-                                &obj->link.inet6_addr_gen_mode_inv,
-                                &af_inet6_addr_gen_mode_valid);
-                break;
-            }
-        }
-    }
-
-    switch (obj->link.type) {
-    case NM_LINK_TYPE_BRIDGE:
-        lnk_data = _parse_lnk_bridge(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_GRE:
-    case NM_LINK_TYPE_GRETAP:
-        lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_INFINIBAND:
-        lnk_data = _parse_lnk_infiniband(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_IP6TNL:
-        lnk_data = _parse_lnk_ip6tnl(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_IP6GRE:
-    case NM_LINK_TYPE_IP6GRETAP:
-        lnk_data = _parse_lnk_ip6gre(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_IPIP:
-        lnk_data = _parse_lnk_ipip(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_MACSEC:
-        lnk_data = _parse_lnk_macsec(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_MACVLAN:
-    case NM_LINK_TYPE_MACVTAP:
-        lnk_data = _parse_lnk_macvlan(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_SIT:
-        lnk_data = _parse_lnk_sit(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_TUN:
-        lnk_data = _parse_lnk_tun(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_VLAN:
-        lnk_data = _parse_lnk_vlan(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_VRF:
-        lnk_data = _parse_lnk_vrf(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_VXLAN:
-        lnk_data = _parse_lnk_vxlan(nl_info_kind, nl_info_data);
-        break;
-    case NM_LINK_TYPE_WIFI:
-    case NM_LINK_TYPE_OLPC_MESH:
-    case NM_LINK_TYPE_WPAN:
-        need_ext_data                = TRUE;
-        lnk_data_complete_from_cache = FALSE;
-        break;
-    case NM_LINK_TYPE_WIREGUARD:
-        lnk_data_complete_from_cache = TRUE;
-        break;
-    default:
-        lnk_data_complete_from_cache = FALSE;
-        break;
-    }
-
-    if (completed_from_cache
-        && (lnk_data_complete_from_cache || need_ext_data || address_complete_from_cache
-            || broadcast_complete_from_cache || !af_inet6_token_valid
-            || !af_inet6_addr_gen_mode_valid || !tb[IFLA_STATS64])) {
-        _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached);
-        if (link_cached && link_cached->_link.netlink.is_in_netlink) {
-            if (lnk_data_complete_from_cache && link_cached->link.type == obj->link.type
-                && link_cached->_link.netlink.lnk
-                && (!lnk_data || nmp_object_equal(lnk_data, link_cached->_link.netlink.lnk))) {
-                /* We always try to look into the cache and reuse the object there.
-                 * We do that, because we consider the lnk object as immutable and don't
-                 * modify it after creating. Hence we can share it and reuse.
-                 *
-                 * Also, sometimes the info-data is missing for updates. In this case
-                 * we want to keep the previously received lnk_data. */
-                nmp_object_unref(lnk_data);
-                lnk_data = nmp_object_ref(link_cached->_link.netlink.lnk);
-            }
-
-            if (need_ext_data && link_cached->link.type == obj->link.type
-                && link_cached->_link.ext_data) {
-                /* Prefer reuse of existing ext_data object */
-                obj->_link.ext_data = g_object_ref(link_cached->_link.ext_data);
-            }
-
-            if (address_complete_from_cache)
-                obj->link.l_address = link_cached->link.l_address;
-            if (broadcast_complete_from_cache)
-                obj->link.l_broadcast = link_cached->link.l_broadcast;
-            if (!af_inet6_token_valid)
-                obj->link.inet6_token = link_cached->link.inet6_token;
-            if (!af_inet6_addr_gen_mode_valid)
-                obj->link.inet6_addr_gen_mode_inv = link_cached->link.inet6_addr_gen_mode_inv;
-            if (!tb[IFLA_STATS64]) {
-                obj->link.rx_packets = link_cached->link.rx_packets;
-                obj->link.rx_bytes   = link_cached->link.rx_bytes;
-                obj->link.tx_packets = link_cached->link.tx_packets;
-                obj->link.tx_bytes   = link_cached->link.tx_bytes;
-            }
-        }
-    }
-
-    obj->_link.netlink.lnk = lnk_data;
-
-    if (need_ext_data && obj->_link.ext_data == NULL) {
-        switch (obj->link.type) {
-        case NM_LINK_TYPE_WIFI:
-        case NM_LINK_TYPE_OLPC_MESH:
-            obj->_link.ext_data =
-                (GObject *) nm_wifi_utils_new(ifi->ifi_index,
-                                              _genl_sock(NM_LINUX_PLATFORM(platform)),
-                                              TRUE);
-            break;
-        case NM_LINK_TYPE_WPAN:
-            obj->_link.ext_data =
-                (GObject *) nm_wpan_utils_new(ifi->ifi_index,
-                                              _genl_sock(NM_LINUX_PLATFORM(platform)),
-                                              TRUE);
-            break;
-        default:
-            g_assert_not_reached();
-        }
-    }
-
-    if (obj->link.type == NM_LINK_TYPE_WIREGUARD) {
-        const NMPObject *lnk_data_new = NULL;
-        struct nl_sock * genl         = NM_LINUX_PLATFORM_GET_PRIVATE(platform)->genl;
-
-        /* The WireGuard kernel module does not yet send link update
-         * notifications, so we don't actually update the cache. For
-         * now, always refetch link data here. */
-
-        _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached);
-        if (link_cached && link_cached->_link.netlink.is_in_netlink
-            && link_cached->link.type == NM_LINK_TYPE_WIREGUARD)
-            obj->_link.wireguard_family_id = link_cached->_link.wireguard_family_id;
-        else
-            obj->_link.wireguard_family_id = -1;
-
-        if (obj->_link.wireguard_family_id < 0)
-            obj->_link.wireguard_family_id = genl_ctrl_resolve(genl, "wireguard");
-
-        if (obj->_link.wireguard_family_id >= 0) {
-            lnk_data_new = _wireguard_read_info(platform,
-                                                genl,
-                                                obj->_link.wireguard_family_id,
-                                                obj->link.ifindex);
-        }
-
-        if (lnk_data_new && obj->_link.netlink.lnk
-            && nmp_object_equal(obj->_link.netlink.lnk, lnk_data_new))
-            nmp_object_unref(lnk_data_new);
-        else {
-            nmp_object_unref(obj->_link.netlink.lnk);
-            obj->_link.netlink.lnk = lnk_data_new;
-        }
-    }
-
-    obj->_link.netlink.is_in_netlink = TRUE;
-    return g_steal_pointer(&obj);
-}
-
-/* Copied and heavily modified from libnl3's addr_msg_parser(). */
-static NMPObject *
-_new_from_nl_addr(struct nlmsghdr *nlh, gboolean id_only)
-{
-    static const struct nla_policy policy[] = {
-        [IFA_LABEL]     = {.type = NLA_STRING, .maxlen = IFNAMSIZ},
-        [IFA_CACHEINFO] = {.minlen = nm_offsetofend(struct ifa_cacheinfo, tstamp)},
-        [IFA_FLAGS]     = {},
-    };
-    struct nlattr *         tb[G_N_ELEMENTS(policy)];
-    const struct ifaddrmsg *ifa;
-    gboolean                is_v4;
-    nm_auto_nmpobj NMPObject *obj = NULL;
-    int                       addr_len;
-    guint32                   lifetime, preferred, timestamp;
-
-    if (!nlmsg_valid_hdr(nlh, sizeof(*ifa)))
-        return NULL;
-
-    ifa = nlmsg_data(nlh);
-
-    if (!NM_IN_SET(ifa->ifa_family, AF_INET, AF_INET6))
-        return NULL;
-
-    is_v4 = ifa->ifa_family == AF_INET;
-
-    if (nlmsg_parse_arr(nlh, sizeof(*ifa), tb, policy) < 0)
-        return NULL;
-
-    addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr);
-
-    if (ifa->ifa_prefixlen > (is_v4 ? 32 : 128))
-        return NULL;
-
-    /*****************************************************************/
-
-    obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, NULL);
-
-    obj->ip_address.ifindex = ifa->ifa_index;
-    obj->ip_address.plen    = ifa->ifa_prefixlen;
-
-    _check_addr_or_return_null(tb, IFA_ADDRESS, addr_len);
-    _check_addr_or_return_null(tb, IFA_LOCAL, addr_len);
-    if (is_v4) {
-        /* For IPv4, kernel omits IFA_LOCAL/IFA_ADDRESS if (and only if) they
-         * are effectively 0.0.0.0 (all-zero). */
-        if (tb[IFA_LOCAL])
-            memcpy(&obj->ip4_address.address, nla_data(tb[IFA_LOCAL]), addr_len);
-        if (tb[IFA_ADDRESS])
-            memcpy(&obj->ip4_address.peer_address, nla_data(tb[IFA_ADDRESS]), addr_len);
-
-        _check_addr_or_return_null(tb, IFA_BROADCAST, addr_len);
-        obj->ip4_address.broadcast_address =
-            tb[IFA_BROADCAST] ? nla_get_u32(tb[IFA_BROADCAST]) : 0u;
-        obj->ip4_address.use_ip4_broadcast_address = TRUE;
-    } else {
-        /* For IPv6, IFA_ADDRESS is always present.
-         *
-         * If IFA_LOCAL is missing, IFA_ADDRESS is @address and @peer_address
-         * is :: (all-zero).
-         *
-         * If unexpectedly IFA_ADDRESS is missing, make the best of it -- but it _should_
-         * actually be there. */
-        if (tb[IFA_ADDRESS] || tb[IFA_LOCAL]) {
-            if (tb[IFA_LOCAL]) {
-                memcpy(&obj->ip6_address.address, nla_data(tb[IFA_LOCAL]), addr_len);
-                if (tb[IFA_ADDRESS])
-                    memcpy(&obj->ip6_address.peer_address, nla_data(tb[IFA_ADDRESS]), addr_len);
-                else
-                    obj->ip6_address.peer_address = obj->ip6_address.address;
-            } else
-                memcpy(&obj->ip6_address.address, nla_data(tb[IFA_ADDRESS]), addr_len);
-        }
-    }
-
-    obj->ip_address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL;
-
-    obj->ip_address.n_ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifa->ifa_flags;
-
-    if (is_v4) {
-        if (tb[IFA_LABEL]) {
-            char label[IFNAMSIZ];
-
-            nla_strlcpy(label, tb[IFA_LABEL], IFNAMSIZ);
-
-            /* Check for ':'; we're only interested in labels used as interface aliases */
-            if (strchr(label, ':'))
-                g_strlcpy(obj->ip4_address.label, label, sizeof(obj->ip4_address.label));
-        }
-    }
-
-    lifetime  = NM_PLATFORM_LIFETIME_PERMANENT;
-    preferred = NM_PLATFORM_LIFETIME_PERMANENT;
-    timestamp = 0;
-    /* IPv6 only */
-    if (tb[IFA_CACHEINFO]) {
-        const struct ifa_cacheinfo *ca;
-
-        ca        = nla_data_as(struct ifa_cacheinfo, tb[IFA_CACHEINFO]);
-        lifetime  = ca->ifa_valid;
-        preferred = ca->ifa_prefered;
-        timestamp = ca->tstamp;
-    }
-    _addrtime_get_lifetimes(timestamp,
-                            lifetime,
-                            preferred,
-                            &obj->ip_address.timestamp,
-                            &obj->ip_address.lifetime,
-                            &obj->ip_address.preferred);
-
-    return g_steal_pointer(&obj);
-}
-
-/* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */
-static NMPObject *
-_new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only)
-{
-    static const struct nla_policy policy[] = {
-        [RTA_TABLE]     = {.type = NLA_U32},
-        [RTA_IIF]       = {.type = NLA_U32},
-        [RTA_OIF]       = {.type = NLA_U32},
-        [RTA_PRIORITY]  = {.type = NLA_U32},
-        [RTA_PREF]      = {.type = NLA_U8},
-        [RTA_FLOW]      = {.type = NLA_U32},
-        [RTA_CACHEINFO] = {.minlen = nm_offsetofend(struct rta_cacheinfo, rta_tsage)},
-        [RTA_METRICS]   = {.type = NLA_NESTED},
-        [RTA_MULTIPATH] = {.type = NLA_NESTED},
-    };
-    const struct rtmsg *rtm;
-    struct nlattr *     tb[G_N_ELEMENTS(policy)];
-    gboolean            is_v4;
-    nm_auto_nmpobj NMPObject *obj = NULL;
-    int                       addr_len;
-    struct {
-        gboolean is_present;
-        int      ifindex;
-        NMIPAddr gateway;
-    } nh = {
-        .is_present = FALSE,
-    };
-    guint32 mss;
-    guint32 window   = 0;
-    guint32 cwnd     = 0;
-    guint32 initcwnd = 0;
-    guint32 initrwnd = 0;
-    guint32 mtu      = 0;
-    guint32 lock     = 0;
-
-    if (!nlmsg_valid_hdr(nlh, sizeof(*rtm)))
-        return NULL;
-
-    rtm = nlmsg_data(nlh);
-
-    /*****************************************************************
-     * only handle ~supported~ routes.
-     *****************************************************************/
-
-    if (!NM_IN_SET(rtm->rtm_family, AF_INET, AF_INET6))
-        return NULL;
-
-    if (!NM_IN_SET(rtm->rtm_type, RTN_UNICAST, RTN_LOCAL))
-        return NULL;
-
-    if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0)
-        return NULL;
-
-    /*****************************************************************/
-
-    is_v4    = rtm->rtm_family == AF_INET;
-    addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr);
-
-    if (rtm->rtm_dst_len > (is_v4 ? 32 : 128))
-        return NULL;
-
-    /*****************************************************************
-     * parse nexthops. Only handle routes with one nh.
-     *****************************************************************/
-
-    if (tb[RTA_MULTIPATH]) {
-        size_t            tlen = nla_len(tb[RTA_MULTIPATH]);
-        struct rtnexthop *rtnh;
-
-        if (tlen < sizeof(*rtnh))
-            goto rta_multipath_done;
-
-        rtnh = nla_data_as(struct rtnexthop, tb[RTA_MULTIPATH]);
-
-        if (tlen < rtnh->rtnh_len)
-            goto rta_multipath_done;
-
-        while (TRUE) {
-            if (nh.is_present) {
-                /* we don't support multipath routes. */
-                return NULL;
-            }
-
-            nh.is_present = TRUE;
-            nh.ifindex    = rtnh->rtnh_ifindex;
-
-            if (rtnh->rtnh_len > sizeof(*rtnh)) {
-                struct nlattr *ntb[G_N_ELEMENTS(policy)];
-
-                if (nla_parse_arr(ntb,
-                                  (struct nlattr *) RTNH_DATA(rtnh),
-                                  rtnh->rtnh_len - sizeof(*rtnh),
-                                  policy)
-                    < 0)
-                    return NULL;
-
-                if (_check_addr_or_return_null(ntb, RTA_GATEWAY, addr_len))
-                    memcpy(&nh.gateway, nla_data(ntb[RTA_GATEWAY]), addr_len);
-            }
-
-            if (tlen < RTNH_ALIGN(rtnh->rtnh_len) + sizeof(*rtnh))
-                goto rta_multipath_done;
-
-            tlen -= RTNH_ALIGN(rtnh->rtnh_len);
-            rtnh = RTNH_NEXT(rtnh);
-        }
-rta_multipath_done:;
-    }
-
-    if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW]) {
-        int      ifindex = 0;
-        NMIPAddr gateway = {};
-
-        if (tb[RTA_OIF])
-            ifindex = nla_get_u32(tb[RTA_OIF]);
-        if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len))
-            memcpy(&gateway, nla_data(tb[RTA_GATEWAY]), addr_len);
-
-        if (!nh.is_present) {
-            /* If no nexthops have been provided via RTA_MULTIPATH
-             * we add it as regular nexthop to maintain backwards
-             * compatibility */
-            nh.ifindex = ifindex;
-            nh.gateway = gateway;
-        } else {
-            /* Kernel supports new style nexthop configuration,
-             * verify that it is a duplicate and ignore old-style nexthop. */
-            if (nh.ifindex != ifindex || memcmp(&nh.gateway, &gateway, addr_len) != 0)
-                return NULL;
-        }
-    } else if (!nh.is_present)
-        return NULL;
-
-    /*****************************************************************/
-
-    mss = 0;
-    if (tb[RTA_METRICS]) {
-        static const struct nla_policy rtax_policy[] = {
-            [RTAX_LOCK]     = {.type = NLA_U32},
-            [RTAX_ADVMSS]   = {.type = NLA_U32},
-            [RTAX_WINDOW]   = {.type = NLA_U32},
-            [RTAX_CWND]     = {.type = NLA_U32},
-            [RTAX_INITCWND] = {.type = NLA_U32},
-            [RTAX_INITRWND] = {.type = NLA_U32},
-            [RTAX_MTU]      = {.type = NLA_U32},
-        };
-        struct nlattr *mtb[G_N_ELEMENTS(rtax_policy)];
-
-        if (nla_parse_nested_arr(mtb, tb[RTA_METRICS], rtax_policy) < 0)
-            return NULL;
-
-        if (mtb[RTAX_LOCK])
-            lock = nla_get_u32(mtb[RTAX_LOCK]);
-        if (mtb[RTAX_ADVMSS])
-            mss = nla_get_u32(mtb[RTAX_ADVMSS]);
-        if (mtb[RTAX_WINDOW])
-            window = nla_get_u32(mtb[RTAX_WINDOW]);
-        if (mtb[RTAX_CWND])
-            cwnd = nla_get_u32(mtb[RTAX_CWND]);
-        if (mtb[RTAX_INITCWND])
-            initcwnd = nla_get_u32(mtb[RTAX_INITCWND]);
-        if (mtb[RTAX_INITRWND])
-            initrwnd = nla_get_u32(mtb[RTAX_INITRWND]);
-        if (mtb[RTAX_MTU])
-            mtu = nla_get_u32(mtb[RTAX_MTU]);
-    }
-
-    /*****************************************************************/
-
-    obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL);
-
-    obj->ip_route.type_coerced  = nm_platform_route_type_coerce(rtm->rtm_type);
-    obj->ip_route.table_coerced = nm_platform_route_table_coerce(
-        tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : (guint32) rtm->rtm_table);
-
-    obj->ip_route.ifindex = nh.ifindex;
-
-    if (_check_addr_or_return_null(tb, RTA_DST, addr_len))
-        memcpy(obj->ip_route.network_ptr, nla_data(tb[RTA_DST]), addr_len);
-
-    obj->ip_route.plen = rtm->rtm_dst_len;
-
-    if (tb[RTA_PRIORITY])
-        obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]);
-
-    if (is_v4)
-        obj->ip4_route.gateway = nh.gateway.addr4;
-    else
-        obj->ip6_route.gateway = nh.gateway.addr6;
-
-    if (is_v4)
-        obj->ip4_route.scope_inv = nm_platform_route_scope_inv(rtm->rtm_scope);
-
-    if (_check_addr_or_return_null(tb, RTA_PREFSRC, addr_len)) {
-        if (is_v4)
-            memcpy(&obj->ip4_route.pref_src, nla_data(tb[RTA_PREFSRC]), addr_len);
-        else
-            memcpy(&obj->ip6_route.pref_src, nla_data(tb[RTA_PREFSRC]), addr_len);
-    }
-
-    if (is_v4)
-        obj->ip4_route.tos = rtm->rtm_tos;
-    else {
-        if (tb[RTA_SRC]) {
-            _check_addr_or_return_null(tb, RTA_SRC, addr_len);
-            memcpy(&obj->ip6_route.src, nla_data(tb[RTA_SRC]), addr_len);
-        }
-        obj->ip6_route.src_plen = rtm->rtm_src_len;
-    }
-
-    obj->ip_route.mss           = mss;
-    obj->ip_route.window        = window;
-    obj->ip_route.cwnd          = cwnd;
-    obj->ip_route.initcwnd      = initcwnd;
-    obj->ip_route.initrwnd      = initrwnd;
-    obj->ip_route.mtu           = mtu;
-    obj->ip_route.lock_window   = NM_FLAGS_HAS(lock, 1 << RTAX_WINDOW);
-    obj->ip_route.lock_cwnd     = NM_FLAGS_HAS(lock, 1 << RTAX_CWND);
-    obj->ip_route.lock_initcwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITCWND);
-    obj->ip_route.lock_initrwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITRWND);
-    obj->ip_route.lock_mtu      = NM_FLAGS_HAS(lock, 1 << RTAX_MTU);
-
-    if (!is_v4) {
-        if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)) {
-            /* Detect support for RTA_PREF by inspecting the netlink message.
-             * RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */
-            _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF,
-                                             tb[RTA_PREF] ? 1 : -1);
-        }
-
-        if (tb[RTA_PREF])
-            obj->ip6_route.rt_pref = nla_get_u8(tb[RTA_PREF]);
-    }
-
-    obj->ip_route.r_rtm_flags = rtm->rtm_flags;
-    obj->ip_route.rt_source   = nmp_utils_ip_config_source_from_rtprot(rtm->rtm_protocol);
-
-    return g_steal_pointer(&obj);
-}
-
-static NMPObject *
-_new_from_nl_routing_rule(struct nlmsghdr *nlh, gboolean id_only)
-{
-    static const struct nla_policy policy[] = {
-        [FRA_UNSPEC] = {},
-        [FRA_DST]    = {/* struct in_addr, struct in6_addr */},
-        [FRA_SRC]    = {/* struct in_addr, struct in6_addr */},
-        [FRA_IIFNAME] =
-            {
-                .type   = NLA_STRING,
-                .maxlen = IFNAMSIZ,
-            },
-        [FRA_GOTO] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_UNUSED2] = {},
-        [FRA_PRIORITY] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_UNUSED3] = {},
-        [FRA_UNUSED4] = {},
-        [FRA_UNUSED5] = {},
-        [FRA_FWMARK] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_FLOW] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_TUN_ID] =
-            {
-                .type = NLA_U64,
-            },
-        [FRA_SUPPRESS_IFGROUP] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_SUPPRESS_PREFIXLEN] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_TABLE] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_FWMASK] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_OIFNAME] =
-            {
-                .type   = NLA_STRING,
-                .maxlen = IFNAMSIZ,
-            },
-        [FRA_PAD] =
-            {
-                .type = NLA_U32,
-            },
-        [FRA_L3MDEV] =
-            {
-                .type = NLA_U8,
-            },
-        [FRA_UID_RANGE] =
-            {
-                .minlen = sizeof(NMFibRuleUidRange),
-                .maxlen = sizeof(NMFibRuleUidRange),
-            },
-        [FRA_PROTOCOL] =
-            {
-                .type = NLA_U8,
-            },
-        [FRA_IP_PROTO] =
-            {
-                .type = NLA_U8,
-            },
-        [FRA_SPORT_RANGE] =
-            {
-                .minlen = sizeof(NMFibRulePortRange),
-                .maxlen = sizeof(NMFibRulePortRange),
-            },
-        [FRA_DPORT_RANGE] =
-            {
-                .minlen = sizeof(NMFibRulePortRange),
-                .maxlen = sizeof(NMFibRulePortRange),
-            },
-    };
-    struct nlattr *            tb[G_N_ELEMENTS(policy)];
-    const struct fib_rule_hdr *frh;
-    NMPlatformRoutingRule *    props;
-    nm_auto_nmpobj NMPObject *obj = NULL;
-    int                       addr_family;
-    guint8                    addr_size;
-
-    if (nlmsg_parse_arr(nlh, sizeof(*frh), tb, policy) < 0)
-        return NULL;
-
-    frh = nlmsg_data(nlh);
-
-    addr_family = frh->family;
-
-    if (!NM_IN_SET(addr_family, AF_INET, AF_INET6)) {
-        /* we don't care about other address families. */
-        return NULL;
-    }
-
-    addr_size = nm_utils_addr_family_to_size(addr_family);
-
-    obj   = nmp_object_new(NMP_OBJECT_TYPE_ROUTING_RULE, NULL);
-    props = &obj->routing_rule;
-
-    props->addr_family = addr_family;
-    props->action      = frh->action;
-    props->flags       = frh->flags;
-    props->tos         = frh->tos;
-
-    props->table = tb[FRA_TABLE] ? nla_get_u32(tb[FRA_TABLE]) : frh->table;
-
-    if (tb[FRA_SUPPRESS_PREFIXLEN])
-        props->suppress_prefixlen_inverse = ~nla_get_u32(tb[FRA_SUPPRESS_PREFIXLEN]);
-
-    if (tb[FRA_SUPPRESS_IFGROUP])
-        props->suppress_ifgroup_inverse = ~nla_get_u32(tb[FRA_SUPPRESS_IFGROUP]);
-
-    if (tb[FRA_IIFNAME])
-        nla_strlcpy(props->iifname, tb[FRA_IIFNAME], sizeof(props->iifname));
-
-    if (tb[FRA_OIFNAME])
-        nla_strlcpy(props->oifname, tb[FRA_OIFNAME], sizeof(props->oifname));
-
-    if (tb[FRA_PRIORITY])
-        props->priority = nla_get_u32(tb[FRA_PRIORITY]);
-
-    if (tb[FRA_FWMARK])
-        props->fwmark = nla_get_u32(tb[FRA_FWMARK]);
-
-    if (tb[FRA_FWMASK])
-        props->fwmask = nla_get_u32(tb[FRA_FWMASK]);
-
-    if (tb[FRA_GOTO])
-        props->goto_target = nla_get_u32(tb[FRA_GOTO]);
-
-    props->src_len = frh->src_len;
-    if (props->src_len > addr_size * 8)
-        return NULL;
-    if (!tb[FRA_SRC]) {
-        if (props->src_len > 0)
-            return NULL;
-    } else if (!nm_ip_addr_set_from_untrusted(addr_family,
-                                              &props->src,
-                                              nla_data(tb[FRA_SRC]),
-                                              nla_len(tb[FRA_SRC]),
-                                              NULL))
-        return NULL;
-
-    props->dst_len = frh->dst_len;
-    if (props->dst_len > addr_size * 8)
-        return NULL;
-    if (!tb[FRA_DST]) {
-        if (props->dst_len > 0)
-            return NULL;
-    } else if (!nm_ip_addr_set_from_untrusted(addr_family,
-                                              &props->dst,
-                                              nla_data(tb[FRA_DST]),
-                                              nla_len(tb[FRA_DST]),
-                                              NULL))
-        return NULL;
-
-    if (tb[FRA_FLOW])
-        props->flow = nla_get_u32(tb[FRA_FLOW]);
-
-    if (tb[FRA_TUN_ID])
-        props->tun_id = nla_get_be64(tb[FRA_TUN_ID]);
-
-    if (tb[FRA_L3MDEV]) {
-        if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) {
-            /* support for FRA_L3MDEV was added in 96c63fa7393d0a346acfe5a91e0c7d4c7782641b,
-             * kernel 4.8, 3 October 2017.
-             *
-             * We can only detect support if the attribute is present. A missing attribute
-             * is not conclusive. */
-            _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, 1);
-        }
-
-        /* actually, kernel only allows this attribute to be missing or
-         * "1". Still, encode it as full uint8.
-         *
-         * Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */
-        props->l3mdev = nla_get_u8(tb[FRA_L3MDEV]);
-    }
-
-    if (tb[FRA_PROTOCOL])
-        props->protocol = nla_get_u8(tb[FRA_PROTOCOL]);
-    else
-        nm_assert(props->protocol == RTPROT_UNSPEC);
-
-    if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)) {
-        /* FRA_PROTOCOL was added in kernel 4.17, dated 3 June, 2018.
-         * See commit 1b71af6053af1bd2f849e9fda4f71c1e3f145dcf. */
-        _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL,
-                                         tb[FRA_PROTOCOL] ? 1 : -1);
-    }
-
-    if (tb[FRA_IP_PROTO])
-        props->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]);
-
-    G_STATIC_ASSERT_EXPR(sizeof(NMFibRulePortRange) == 4);
-    G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRulePortRange, start) == 0);
-    G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRulePortRange, end) == 2);
-
-    nla_memcpy_checked_size(&props->sport_range, tb[FRA_SPORT_RANGE], sizeof(props->sport_range));
-    nla_memcpy_checked_size(&props->dport_range, tb[FRA_DPORT_RANGE], sizeof(props->dport_range));
-
-    if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) {
-        /* support for FRA_IP_PROTO, FRA_SPORT_RANGE, and FRA_DPORT_RANGE was added together
-         * by bfff4862653bb96001ab57c1edd6d03f48e5f035, kernel 4.17, 4 June 2018.
-         *
-         * Unfortunately, a missing attribute does not tell us anything about support.
-         * We can only tell for sure when we have support, but not when we don't have.  */
-        if (tb[FRA_IP_PROTO] || tb[FRA_SPORT_RANGE] || tb[FRA_DPORT_RANGE])
-            _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO, 1);
-    }
-
-    G_STATIC_ASSERT_EXPR(sizeof(NMFibRuleUidRange) == 8);
-    G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRuleUidRange, start) == 0);
-    G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRuleUidRange, end) == 4);
-
-    if (tb[FRA_UID_RANGE]) {
-        if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) {
-            /* support for FRA_UID_RANGE was added in 622ec2c9d52405973c9f1ca5116eb1c393adfc7d,
-             * kernel 4.10, 19 February 2017.
-             *
-             * We can only detect support if the attribute is present. A missing attribute
-             * is not conclusive. */
-            _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, 1);
-        }
-
-        nla_memcpy_checked_size(&props->uid_range, tb[FRA_UID_RANGE], sizeof(props->uid_range));
-        props->uid_range_has = TRUE;
-    }
-
-    return g_steal_pointer(&obj);
-}
-
-static guint32
-psched_tick_to_time(NMPlatform *platform, guint32 tick)
-{
-    static gboolean initialized;
-    static double   tick_in_usec = 1;
-
-    if (!initialized) {
-        gs_free char *params       = NULL;
-        double        clock_factor = 1;
-        guint32       clock_res;
-        guint32       t2us;
-        guint32       us2t;
-
-        initialized = TRUE;
-        params = nm_platform_sysctl_get(platform, NMP_SYSCTL_PATHID_ABSOLUTE("/proc/net/psched"));
-        if (!params || sscanf(params, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) {
-            _LOGW("packet scheduler parameters not available");
-        } else {
-            /* See tc_core_init() in iproute2 */
-            if (clock_res == 1000000000)
-                t2us = us2t;
-
-            clock_factor = (double) clock_res / PSCHED_TIME_UNITS_PER_SEC;
-            tick_in_usec = (double) t2us / us2t * clock_factor;
-        }
-    }
-
-    return tick / tick_in_usec;
-}
-
-static NMPObject *
-_new_from_nl_qdisc(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only)
-{
-    static const struct nla_policy policy[] = {
-        [TCA_KIND]    = {.type = NLA_STRING},
-        [TCA_OPTIONS] = {.type = NLA_NESTED},
-    };
-    struct nlattr *     tb[G_N_ELEMENTS(policy)];
-    const struct tcmsg *tcm;
-    nm_auto_nmpobj NMPObject *obj = NULL;
-
-    if (nlmsg_parse_arr(nlh, sizeof(*tcm), tb, policy) < 0)
-        return NULL;
-
-    if (!tb[TCA_KIND])
-        return NULL;
-
-    tcm = nlmsg_data(nlh);
-
-    obj = nmp_object_new(NMP_OBJECT_TYPE_QDISC, NULL);
-
-    obj->qdisc.kind        = g_intern_string(nla_get_string(tb[TCA_KIND]));
-    obj->qdisc.ifindex     = tcm->tcm_ifindex;
-    obj->qdisc.addr_family = tcm->tcm_family;
-    obj->qdisc.handle      = tcm->tcm_handle;
-    obj->qdisc.parent      = tcm->tcm_parent;
-    obj->qdisc.info        = tcm->tcm_info;
-
-    if (nm_streq0(obj->qdisc.kind, "fq_codel")) {
-        obj->qdisc.fq_codel.memory_limit = NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET;
-        obj->qdisc.fq_codel.ce_threshold = NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED;
-    }
-
-    if (tb[TCA_OPTIONS]) {
-        struct nlattr *options_attr;
-        int            remaining;
-
-        if (nm_streq0(obj->qdisc.kind, "sfq")) {
-            struct tc_sfq_qopt_v1 opt;
-
-            if (tb[TCA_OPTIONS]->nla_len >= nla_attr_size(sizeof(opt))) {
-                memcpy(&opt, nla_data(tb[TCA_OPTIONS]), sizeof(opt));
-                obj->qdisc.sfq.quantum        = opt.v0.quantum;
-                obj->qdisc.sfq.perturb_period = opt.v0.perturb_period;
-                obj->qdisc.sfq.limit          = opt.v0.limit;
-                obj->qdisc.sfq.divisor        = opt.v0.divisor;
-                obj->qdisc.sfq.flows          = opt.v0.flows;
-                obj->qdisc.sfq.depth          = opt.depth;
-            }
-        } else if (nm_streq0(obj->qdisc.kind, "tbf")) {
-            static const struct nla_policy tbf_policy[] = {
-                [TCA_TBF_PARMS]  = {.minlen = sizeof(struct tc_tbf_qopt)},
-                [TCA_TBF_RATE64] = {.type = NLA_U64},
-            };
-            struct nlattr *    tbf_tb[G_N_ELEMENTS(tbf_policy)];
-            struct tc_tbf_qopt opt;
-
-            if (nla_parse_nested_arr(tbf_tb, tb[TCA_OPTIONS], tbf_policy) < 0)
-                return NULL;
-            if (!tbf_tb[TCA_TBF_PARMS])
-                return NULL;
-
-            nla_memcpy_checked_size(&opt, tbf_tb[TCA_TBF_PARMS], sizeof(opt));
-            obj->qdisc.tbf.rate = opt.rate.rate;
-            if (tbf_tb[TCA_TBF_RATE64])
-                obj->qdisc.tbf.rate = nla_get_u64(tbf_tb[TCA_TBF_RATE64]);
-            obj->qdisc.tbf.burst =
-                ((double) obj->qdisc.tbf.rate * psched_tick_to_time(platform, opt.buffer))
-                / PSCHED_TIME_UNITS_PER_SEC;
-            obj->qdisc.tbf.limit = opt.limit;
-        } else {
-            nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) {
-                if (nla_len(options_attr) < sizeof(uint32_t))
-                    continue;
-
-                if (nm_streq0(obj->qdisc.kind, "fq_codel")) {
-                    switch (nla_type(options_attr)) {
-                    case TCA_FQ_CODEL_LIMIT:
-                        obj->qdisc.fq_codel.limit = nla_get_u32(options_attr);
-                        break;
-                    case TCA_FQ_CODEL_FLOWS:
-                        obj->qdisc.fq_codel.flows = nla_get_u32(options_attr);
-                        break;
-                    case TCA_FQ_CODEL_TARGET:
-                        obj->qdisc.fq_codel.target = nla_get_u32(options_attr);
-                        break;
-                    case TCA_FQ_CODEL_INTERVAL:
-                        obj->qdisc.fq_codel.interval = nla_get_u32(options_attr);
-                        break;
-                    case TCA_FQ_CODEL_QUANTUM:
-                        obj->qdisc.fq_codel.quantum = nla_get_u32(options_attr);
-                        break;
-                    case TCA_FQ_CODEL_CE_THRESHOLD:
-                        obj->qdisc.fq_codel.ce_threshold = nla_get_u32(options_attr);
-                        break;
-                    case TCA_FQ_CODEL_MEMORY_LIMIT:
-                        obj->qdisc.fq_codel.memory_limit = nla_get_u32(options_attr);
-                        break;
-                    case TCA_FQ_CODEL_ECN:
-                        obj->qdisc.fq_codel.ecn = !!nla_get_u32(options_attr);
-                        break;
-                    }
-                }
-            }
-        }
-    }
-
-    return g_steal_pointer(&obj);
-}
-
-static NMPObject *
-_new_from_nl_tfilter(struct nlmsghdr *nlh, gboolean id_only)
-{
-    static const struct nla_policy policy[] = {
-        [TCA_KIND] = {.type = NLA_STRING},
-    };
-    struct nlattr *     tb[G_N_ELEMENTS(policy)];
-    NMPObject *         obj = NULL;
-    const struct tcmsg *tcm;
-
-    if (nlmsg_parse_arr(nlh, sizeof(*tcm), tb, policy) < 0)
-        return NULL;
-
-    if (!tb[TCA_KIND])
-        return NULL;
-
-    tcm = nlmsg_data(nlh);
-
-    obj = nmp_object_new(NMP_OBJECT_TYPE_TFILTER, NULL);
-
-    obj->tfilter.kind        = g_intern_string(nla_get_string(tb[TCA_KIND]));
-    obj->tfilter.ifindex     = tcm->tcm_ifindex;
-    obj->tfilter.addr_family = tcm->tcm_family;
-    obj->tfilter.handle      = tcm->tcm_handle;
-    obj->tfilter.parent      = tcm->tcm_parent;
-    obj->tfilter.info        = tcm->tcm_info;
-
-    return obj;
-}
-
-/**
- * nmp_object_new_from_nl:
- * @platform: (allow-none): for creating certain objects, the constructor wants to check
- *   sysfs. For this the platform instance is needed. If missing, the object might not
- *   be correctly detected.
- * @cache: (allow-none): for certain objects, the netlink message doesn't contain all the information.
- *   If a cache is given, the object is completed with information from the cache.
- * @nlh: the netlink message header
- * @id_only: whether only to create an empty object with only the ID fields set.
- *
- * Returns: %NULL or a newly created NMPObject instance.
- **/
-static NMPObject *
-nmp_object_new_from_nl(NMPlatform *    platform,
-                       const NMPCache *cache,
-                       struct nl_msg * msg,
-                       gboolean        id_only)
-{
-    struct nlmsghdr *msghdr;
-
-    if (nlmsg_get_proto(msg) != NETLINK_ROUTE)
-        return NULL;
-
-    msghdr = nlmsg_hdr(msg);
-
-    switch (msghdr->nlmsg_type) {
-    case RTM_NEWLINK:
-    case RTM_DELLINK:
-    case RTM_GETLINK:
-    case RTM_SETLINK:
-        return _new_from_nl_link(platform, cache, msghdr, id_only);
-    case RTM_NEWADDR:
-    case RTM_DELADDR:
-    case RTM_GETADDR:
-        return _new_from_nl_addr(msghdr, id_only);
-    case RTM_NEWROUTE:
-    case RTM_DELROUTE:
-    case RTM_GETROUTE:
-        return _new_from_nl_route(msghdr, id_only);
-    case RTM_NEWRULE:
-    case RTM_DELRULE:
-    case RTM_GETRULE:
-        return _new_from_nl_routing_rule(msghdr, id_only);
-    case RTM_NEWQDISC:
-    case RTM_DELQDISC:
-    case RTM_GETQDISC:
-        return _new_from_nl_qdisc(platform, msghdr, id_only);
-    case RTM_NEWTFILTER:
-    case RTM_DELTFILTER:
-    case RTM_GETTFILTER:
-        return _new_from_nl_tfilter(msghdr, id_only);
-    default:
-        return NULL;
-    }
-}
-
-/*****************************************************************************/
-
-static gboolean
-_nl_msg_new_link_set_afspec(struct nl_msg *msg, int addr_gen_mode, NMUtilsIPv6IfaceId *iid)
-{
-    struct nlattr *af_spec;
-    struct nlattr *af_attr;
-
-    nm_assert(msg);
-
-    if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC)))
-        goto nla_put_failure;
-
-    if (addr_gen_mode >= 0 || iid) {
-        if (!(af_attr = nla_nest_start(msg, AF_INET6)))
-            goto nla_put_failure;
-
-        if (addr_gen_mode >= 0)
-            NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode);
-
-        if (iid) {
-            struct in6_addr i6_token = {.s6_addr = {
-                                            0,
-                                        }};
-
-            nm_utils_ipv6_addr_set_interface_identifier(&i6_token, *iid);
-            NLA_PUT(msg, IFLA_INET6_TOKEN, sizeof(struct in6_addr), &i6_token);
-        }
-
-        nla_nest_end(msg, af_attr);
-    }
-
-    nla_nest_end(msg, af_spec);
-
-    return TRUE;
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static gboolean
-_nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpointer extra_data)
-{
-    struct nlattr *info;
-    struct nlattr *data = NULL;
-    const char *   kind;
-
-    nm_assert(msg);
-
-    kind = nm_link_type_to_rtnl_type_string(link_type);
-    if (!kind)
-        goto nla_put_failure;
-
-    if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
-        goto nla_put_failure;
-
-    NLA_PUT_STRING(msg, IFLA_INFO_KIND, kind);
-
-    switch (link_type) {
-    case NM_LINK_TYPE_BRIDGE:
-    {
-        const NMPlatformLnkBridge *props = extra_data;
-
-        nm_assert(extra_data);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        NLA_PUT_U32(msg, IFLA_BR_FORWARD_DELAY, props->forward_delay);
-        NLA_PUT_U32(msg, IFLA_BR_HELLO_TIME, props->hello_time);
-        NLA_PUT_U32(msg, IFLA_BR_MAX_AGE, props->max_age);
-        NLA_PUT_U32(msg, IFLA_BR_AGEING_TIME, props->ageing_time);
-        NLA_PUT_U32(msg, IFLA_BR_STP_STATE, !!props->stp_state);
-        NLA_PUT_U16(msg, IFLA_BR_PRIORITY, props->priority);
-        NLA_PUT_U16(msg, IFLA_BR_VLAN_PROTOCOL, htons(props->vlan_protocol));
-        if (props->vlan_stats_enabled)
-            NLA_PUT_U8(msg, IFLA_BR_VLAN_STATS_ENABLED, !!props->vlan_stats_enabled);
-        NLA_PUT_U16(msg, IFLA_BR_GROUP_FWD_MASK, props->group_fwd_mask);
-        NLA_PUT(msg, IFLA_BR_GROUP_ADDR, sizeof(props->group_addr), &props->group_addr);
-        NLA_PUT_U8(msg, IFLA_BR_MCAST_SNOOPING, !!props->mcast_snooping);
-        NLA_PUT_U8(msg, IFLA_BR_MCAST_ROUTER, props->mcast_router);
-        NLA_PUT_U8(msg, IFLA_BR_MCAST_QUERY_USE_IFADDR, !!props->mcast_query_use_ifaddr);
-        NLA_PUT_U8(msg, IFLA_BR_MCAST_QUERIER, !!props->mcast_querier);
-        NLA_PUT_U32(msg, IFLA_BR_MCAST_HASH_MAX, props->mcast_hash_max);
-        NLA_PUT_U32(msg, IFLA_BR_MCAST_LAST_MEMBER_CNT, props->mcast_last_member_count);
-        NLA_PUT_U32(msg, IFLA_BR_MCAST_STARTUP_QUERY_CNT, props->mcast_startup_query_count);
-        NLA_PUT_U64(msg, IFLA_BR_MCAST_LAST_MEMBER_INTVL, props->mcast_last_member_interval);
-        NLA_PUT_U64(msg, IFLA_BR_MCAST_MEMBERSHIP_INTVL, props->mcast_membership_interval);
-        NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERIER_INTVL, props->mcast_querier_interval);
-        NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERY_INTVL, props->mcast_query_interval);
-        NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, props->mcast_query_response_interval);
-        NLA_PUT_U64(msg, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, props->mcast_startup_query_interval);
-        break;
-    }
-    case NM_LINK_TYPE_VLAN:
-    {
-        const NMPlatformLnkVlan *props = extra_data;
-
-        nm_assert(extra_data);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        NLA_PUT_U16(msg, IFLA_VLAN_ID, props->id);
-
-        {
-            struct ifla_vlan_flags flags = {
-                .flags = props->flags & NM_VLAN_FLAGS_ALL,
-                .mask  = NM_VLAN_FLAGS_ALL,
-            };
-
-            NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
-        }
-        break;
-    }
-    case NM_LINK_TYPE_VRF:
-    {
-        const NMPlatformLnkVrf *props = extra_data;
-
-        nm_assert(extra_data);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        NLA_PUT_U32(msg, IFLA_VRF_TABLE, props->table);
-        break;
-    }
-    case NM_LINK_TYPE_VXLAN:
-    {
-        const NMPlatformLnkVxlan *props = extra_data;
-
-        nm_assert(extra_data);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        NLA_PUT_U32(msg, IFLA_VXLAN_ID, props->id);
-
-        if (props->group)
-            NLA_PUT(msg, IFLA_VXLAN_GROUP, sizeof(props->group), &props->group);
-        else if (!IN6_IS_ADDR_UNSPECIFIED(&props->group6))
-            NLA_PUT(msg, IFLA_VXLAN_GROUP6, sizeof(props->group6), &props->group6);
-
-        if (props->local)
-            NLA_PUT(msg, IFLA_VXLAN_LOCAL, sizeof(props->local), &props->local);
-        else if (!IN6_IS_ADDR_UNSPECIFIED(&props->local6))
-            NLA_PUT(msg, IFLA_VXLAN_LOCAL6, sizeof(props->local6), &props->local6);
-
-        if (props->parent_ifindex >= 0)
-            NLA_PUT_U32(msg, IFLA_VXLAN_LINK, props->parent_ifindex);
-
-        if (props->src_port_min || props->src_port_max) {
-            struct nm_ifla_vxlan_port_range port_range = {
-                .low  = htons(props->src_port_min),
-                .high = htons(props->src_port_max),
-            };
-
-            NLA_PUT(msg, IFLA_VXLAN_PORT_RANGE, sizeof(port_range), &port_range);
-        }
-
-        NLA_PUT_U16(msg, IFLA_VXLAN_PORT, htons(props->dst_port));
-        NLA_PUT_U8(msg, IFLA_VXLAN_TOS, props->tos);
-        NLA_PUT_U8(msg, IFLA_VXLAN_TTL, props->ttl);
-        NLA_PUT_U32(msg, IFLA_VXLAN_AGEING, props->ageing);
-        NLA_PUT_U32(msg, IFLA_VXLAN_LIMIT, props->limit);
-        NLA_PUT_U8(msg, IFLA_VXLAN_LEARNING, !!props->learning);
-        NLA_PUT_U8(msg, IFLA_VXLAN_PROXY, !!props->proxy);
-        NLA_PUT_U8(msg, IFLA_VXLAN_RSC, !!props->rsc);
-        NLA_PUT_U8(msg, IFLA_VXLAN_L2MISS, !!props->l2miss);
-        NLA_PUT_U8(msg, IFLA_VXLAN_L3MISS, !!props->l3miss);
-        break;
-    }
-    case NM_LINK_TYPE_VETH:
-    {
-        const char *           veth_peer = extra_data;
-        const struct ifinfomsg ifi       = {};
-        struct nlattr *        info_peer;
-
-        nm_assert(veth_peer);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-        if (!(info_peer = nla_nest_start(msg, 1 /*VETH_INFO_PEER*/)))
-            goto nla_put_failure;
-        if (nlmsg_append_struct(msg, &ifi) < 0)
-            goto nla_put_failure;
-        NLA_PUT_STRING(msg, IFLA_IFNAME, veth_peer);
-        nla_nest_end(msg, info_peer);
-        break;
-    }
-    case NM_LINK_TYPE_GRE:
-    case NM_LINK_TYPE_GRETAP:
-    {
-        const NMPlatformLnkGre *props = extra_data;
-
-        nm_assert(props);
-        nm_assert(props->is_tap == (link_type == NM_LINK_TYPE_GRETAP));
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        if (props->parent_ifindex)
-            NLA_PUT_U32(msg, IFLA_GRE_LINK, props->parent_ifindex);
-        NLA_PUT_U32(msg, IFLA_GRE_LOCAL, props->local);
-        NLA_PUT_U32(msg, IFLA_GRE_REMOTE, props->remote);
-        NLA_PUT_U8(msg, IFLA_GRE_TTL, props->ttl);
-        NLA_PUT_U8(msg, IFLA_GRE_TOS, props->tos);
-        NLA_PUT_U8(msg, IFLA_GRE_PMTUDISC, !!props->path_mtu_discovery);
-        NLA_PUT_U32(msg, IFLA_GRE_IKEY, htonl(props->input_key));
-        NLA_PUT_U32(msg, IFLA_GRE_OKEY, htonl(props->output_key));
-        NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, htons(props->input_flags));
-        NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, htons(props->output_flags));
-        break;
-    }
-    case NM_LINK_TYPE_SIT:
-    {
-        const NMPlatformLnkSit *props = extra_data;
-
-        nm_assert(props);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        if (props->parent_ifindex)
-            NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex);
-        NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, props->local);
-        NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, props->remote);
-        NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl);
-        NLA_PUT_U8(msg, IFLA_IPTUN_TOS, props->tos);
-        NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery);
-        break;
-    }
-    case NM_LINK_TYPE_IP6TNL:
-    {
-        const NMPlatformLnkIp6Tnl *props = extra_data;
-        guint32                    flowinfo;
-
-        nm_assert(props);
-        nm_assert(!props->is_gre);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        if (props->parent_ifindex)
-            NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex);
-
-        if (!IN6_IS_ADDR_UNSPECIFIED(&props->local))
-            NLA_PUT(msg, IFLA_IPTUN_LOCAL, sizeof(props->local), &props->local);
-        if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote))
-            NLA_PUT(msg, IFLA_IPTUN_REMOTE, sizeof(props->remote), &props->remote);
-
-        NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl);
-        NLA_PUT_U8(msg, IFLA_IPTUN_ENCAP_LIMIT, props->encap_limit);
-
-        flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK;
-        flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) & IP6_FLOWINFO_TCLASS_MASK;
-        NLA_PUT_U32(msg, IFLA_IPTUN_FLOWINFO, htonl(flowinfo));
-        NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, props->proto);
-        NLA_PUT_U32(msg, IFLA_IPTUN_FLAGS, props->flags);
-        break;
-    }
-    case NM_LINK_TYPE_IP6GRE:
-    case NM_LINK_TYPE_IP6GRETAP:
-    {
-        const NMPlatformLnkIp6Tnl *props = extra_data;
-        guint32                    flowinfo;
-
-        nm_assert(props);
-        nm_assert(props->is_gre);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        if (props->parent_ifindex)
-            NLA_PUT_U32(msg, IFLA_GRE_LINK, props->parent_ifindex);
-
-        NLA_PUT_U32(msg, IFLA_GRE_IKEY, htonl(props->input_key));
-        NLA_PUT_U32(msg, IFLA_GRE_OKEY, htonl(props->output_key));
-        NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, htons(props->input_flags));
-        NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, htons(props->output_flags));
-
-        if (!IN6_IS_ADDR_UNSPECIFIED(&props->local))
-            NLA_PUT(msg, IFLA_GRE_LOCAL, sizeof(props->local), &props->local);
-        if (!IN6_IS_ADDR_UNSPECIFIED(&props->local))
-            NLA_PUT(msg, IFLA_GRE_REMOTE, sizeof(props->remote), &props->remote);
-
-        NLA_PUT_U8(msg, IFLA_GRE_TTL, props->ttl);
-        NLA_PUT_U8(msg, IFLA_GRE_ENCAP_LIMIT, props->encap_limit);
-
-        flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK;
-        flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) & IP6_FLOWINFO_TCLASS_MASK;
-        NLA_PUT_U32(msg, IFLA_GRE_FLOWINFO, htonl(flowinfo));
-        NLA_PUT_U32(msg, IFLA_GRE_FLAGS, props->flags);
-        break;
-    }
-    case NM_LINK_TYPE_IPIP:
-    {
-        const NMPlatformLnkIpIp *props = extra_data;
-
-        nm_assert(props);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        if (props->parent_ifindex)
-            NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex);
-        NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, props->local);
-        NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, props->remote);
-        NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl);
-        NLA_PUT_U8(msg, IFLA_IPTUN_TOS, props->tos);
-        NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery);
-        break;
-    }
-    case NM_LINK_TYPE_MACSEC:
-    {
-        const NMPlatformLnkMacsec *props = extra_data;
-
-        nm_assert(props);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        if (props->icv_length)
-            NLA_PUT_U8(msg, IFLA_MACSEC_ICV_LEN, 16);
-        if (props->cipher_suite)
-            NLA_PUT_U64(msg, IFLA_MACSEC_CIPHER_SUITE, props->cipher_suite);
-        if (props->replay_protect)
-            NLA_PUT_U32(msg, IFLA_MACSEC_WINDOW, props->window);
-
-        NLA_PUT_U64(msg, IFLA_MACSEC_SCI, htobe64(props->sci));
-        NLA_PUT_U8(msg, IFLA_MACSEC_ENCODING_SA, props->encoding_sa);
-        NLA_PUT_U8(msg, IFLA_MACSEC_ENCRYPT, props->encrypt);
-        NLA_PUT_U8(msg, IFLA_MACSEC_PROTECT, props->protect);
-        NLA_PUT_U8(msg, IFLA_MACSEC_INC_SCI, props->include_sci);
-        NLA_PUT_U8(msg, IFLA_MACSEC_ES, props->es);
-        NLA_PUT_U8(msg, IFLA_MACSEC_SCB, props->scb);
-        NLA_PUT_U8(msg, IFLA_MACSEC_REPLAY_PROTECT, props->replay_protect);
-        NLA_PUT_U8(msg, IFLA_MACSEC_VALIDATION, props->validation);
-        break;
-    };
-    case NM_LINK_TYPE_MACVTAP:
-    case NM_LINK_TYPE_MACVLAN:
-    {
-        const NMPlatformLnkMacvlan *props = extra_data;
-
-        nm_assert(props);
-
-        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-            goto nla_put_failure;
-
-        NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, props->mode);
-        NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0);
-        break;
-    }
-    default:
-        nm_assert(!extra_data);
-        break;
-    }
-
-    if (data)
-        nla_nest_end(msg, data);
-
-    nla_nest_end(msg, info);
-
-    return TRUE;
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static gboolean
-_nl_msg_new_link_set_linkinfo_vlan(struct nl_msg *         msg,
-                                   int                     vlan_id,
-                                   guint32                 flags_mask,
-                                   guint32                 flags_set,
-                                   const NMVlanQosMapping *ingress_qos,
-                                   int                     ingress_qos_len,
-                                   const NMVlanQosMapping *egress_qos,
-                                   int                     egress_qos_len)
-{
-    struct nlattr *info;
-    struct nlattr *data;
-    guint          i;
-    gboolean       has_any_vlan_properties = FALSE;
-
-    G_STATIC_ASSERT(NM_VLAN_FLAG_REORDER_HEADERS == (guint32) VLAN_FLAG_REORDER_HDR);
-    G_STATIC_ASSERT(NM_VLAN_FLAG_GVRP == (guint32) VLAN_FLAG_GVRP);
-    G_STATIC_ASSERT(NM_VLAN_FLAG_LOOSE_BINDING == (guint32) VLAN_FLAG_LOOSE_BINDING);
-    G_STATIC_ASSERT(NM_VLAN_FLAG_MVRP == (guint32) VLAN_FLAG_MVRP);
-
-#define VLAN_XGRESS_PRIO_VALID(from) (((from) & ~(guint32) 0x07) == 0)
-
-    nm_assert(msg);
-
-    /* We must not create an empty IFLA_LINKINFO section. Otherwise, kernel
-     * rejects the request as invalid. */
-    if (flags_mask != 0 || vlan_id >= 0)
-        has_any_vlan_properties = TRUE;
-    if (!has_any_vlan_properties && ingress_qos && ingress_qos_len > 0) {
-        for (i = 0; i < ingress_qos_len; i++) {
-            if (VLAN_XGRESS_PRIO_VALID(ingress_qos[i].from)) {
-                has_any_vlan_properties = TRUE;
-                break;
-            }
-        }
-    }
-    if (!has_any_vlan_properties && egress_qos && egress_qos_len > 0) {
-        for (i = 0; i < egress_qos_len; i++) {
-            if (VLAN_XGRESS_PRIO_VALID(egress_qos[i].to)) {
-                has_any_vlan_properties = TRUE;
-                break;
-            }
-        }
-    }
-    if (!has_any_vlan_properties)
-        return TRUE;
-
-    if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
-        goto nla_put_failure;
-
-    NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan");
-
-    if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-        goto nla_put_failure;
-
-    if (vlan_id >= 0)
-        NLA_PUT_U16(msg, IFLA_VLAN_ID, vlan_id);
-
-    if (flags_mask != 0) {
-        struct ifla_vlan_flags flags = {
-            .flags = flags_mask & flags_set,
-            .mask  = flags_mask,
-        };
-
-        NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
-    }
-
-    if (ingress_qos && ingress_qos_len > 0) {
-        struct nlattr *qos = NULL;
-
-        for (i = 0; i < ingress_qos_len; i++) {
-            /* Silently ignore invalid mappings. Kernel would truncate
-             * them and modify the wrong mapping. */
-            if (VLAN_XGRESS_PRIO_VALID(ingress_qos[i].from)) {
-                if (!qos) {
-                    if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
-                        goto nla_put_failure;
-                }
-                NLA_PUT(msg, i, sizeof(ingress_qos[i]), &ingress_qos[i]);
-            }
-        }
-
-        if (qos)
-            nla_nest_end(msg, qos);
-    }
-
-    if (egress_qos && egress_qos_len > 0) {
-        struct nlattr *qos = NULL;
-
-        for (i = 0; i < egress_qos_len; i++) {
-            if (VLAN_XGRESS_PRIO_VALID(egress_qos[i].to)) {
-                if (!qos) {
-                    if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
-                        goto nla_put_failure;
-                }
-                NLA_PUT(msg, i, sizeof(egress_qos[i]), &egress_qos[i]);
-            }
-        }
-
-        if (qos)
-            nla_nest_end(msg, qos);
-    }
-
-    nla_nest_end(msg, data);
-    nla_nest_end(msg, info);
-
-    return TRUE;
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static struct nl_msg *
-_nl_msg_new_link_full(int         nlmsg_type,
-                      int         nlmsg_flags,
-                      int         ifindex,
-                      const char *ifname,
-                      guint8      family,
-                      unsigned    flags_mask,
-                      unsigned    flags_set)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    const struct ifinfomsg       ifi = {
-        .ifi_family = family,
-        .ifi_change = flags_mask,
-        .ifi_flags  = flags_set,
-        .ifi_index  = ifindex,
-    };
-
-    nm_assert(NM_IN_SET(nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK, RTM_SETLINK));
-
-    msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags);
-
-    if (nlmsg_append_struct(msg, &ifi) < 0)
-        goto nla_put_failure;
-
-    if (ifname)
-        NLA_PUT_STRING(msg, IFLA_IFNAME, ifname);
-
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static struct nl_msg *
-_nl_msg_new_link(int nlmsg_type, int nlmsg_flags, int ifindex, const char *ifname)
-{
-    return _nl_msg_new_link_full(nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0);
-}
-
-/* Copied and modified from libnl3's build_addr_msg(). */
-static struct nl_msg *
-_nl_msg_new_address(int           nlmsg_type,
-                    int           nlmsg_flags,
-                    int           family,
-                    int           ifindex,
-                    gconstpointer address,
-                    guint8        plen,
-                    gconstpointer peer_address,
-                    guint32       flags,
-                    int           scope,
-                    guint32       lifetime,
-                    guint32       preferred,
-                    in_addr_t     ip4_broadcast_address,
-                    const char *  label)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    struct ifaddrmsg             am  = {
-        .ifa_family    = family,
-        .ifa_index     = ifindex,
-        .ifa_prefixlen = plen,
-        .ifa_flags     = flags,
-    };
-    gsize addr_len;
-
-    nm_assert(NM_IN_SET(family, AF_INET, AF_INET6));
-    nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWADDR, RTM_DELADDR));
-
-    msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags);
-
-    if (scope == -1) {
-        /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */
-        if (family == AF_INET && address && *((char *) address) == 127)
-            scope = RT_SCOPE_HOST;
-        else
-            scope = RT_SCOPE_UNIVERSE;
-    }
-    am.ifa_scope = scope,
-
-    addr_len = family == AF_INET ? sizeof(in_addr_t) : sizeof(struct in6_addr);
-
-    if (nlmsg_append_struct(msg, &am) < 0)
-        goto nla_put_failure;
-
-    if (address)
-        NLA_PUT(msg, IFA_LOCAL, addr_len, address);
-
-    if (peer_address)
-        NLA_PUT(msg, IFA_ADDRESS, addr_len, peer_address);
-    else if (address)
-        NLA_PUT(msg, IFA_ADDRESS, addr_len, address);
-
-    if (label && label[0])
-        NLA_PUT_STRING(msg, IFA_LABEL, label);
-
-    if (ip4_broadcast_address != 0)
-        NLA_PUT(msg, IFA_BROADCAST, sizeof(in_addr_t), &ip4_broadcast_address);
-
-    if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT || preferred != NM_PLATFORM_LIFETIME_PERMANENT) {
-        struct ifa_cacheinfo ca = {
-            .ifa_valid    = lifetime,
-            .ifa_prefered = preferred,
-        };
-
-        NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca);
-    }
-
-    if (flags & ~((guint32) 0xFF)) {
-        /* only set the IFA_FLAGS attribute, if they actually contain additional
-         * flags that are not already set to am.ifa_flags.
-         *
-         * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL
-         * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which
-         * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. */
-        NLA_PUT_U32(msg, IFA_FLAGS, flags);
-    }
-
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static guint32
-ip_route_get_lock_flag(const NMPlatformIPRoute *route)
-{
-    return (((guint32) route->lock_window) << RTAX_WINDOW)
-           | (((guint32) route->lock_cwnd) << RTAX_CWND)
-           | (((guint32) route->lock_initcwnd) << RTAX_INITCWND)
-           | (((guint32) route->lock_initrwnd) << RTAX_INITRWND)
-           | (((guint32) route->lock_mtu) << RTAX_MTU);
-}
-
-/* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */
-static struct nl_msg *
-_nl_msg_new_route(int nlmsg_type, guint16 nlmsgflags, const NMPObject *obj)
-{
-    nm_auto_nlmsg struct nl_msg *msg   = NULL;
-    const NMPClass *             klass = NMP_OBJECT_GET_CLASS(obj);
-    gboolean                     is_v4 = klass->addr_family == AF_INET;
-    const guint32                lock  = ip_route_get_lock_flag(NMP_OBJECT_CAST_IP_ROUTE(obj));
-    const guint32                table =
-        nm_platform_route_table_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->table_coerced, TRUE);
-    const struct rtmsg rtmsg = {
-        .rtm_family   = klass->addr_family,
-        .rtm_tos      = is_v4 ? obj->ip4_route.tos : 0,
-        .rtm_table    = table <= 0xFF ? table : RT_TABLE_UNSPEC,
-        .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot(obj->ip_route.rt_source),
-        .rtm_scope =
-            is_v4 ? nm_platform_route_scope_inv(obj->ip4_route.scope_inv) : RT_SCOPE_NOWHERE,
-        .rtm_type    = nm_platform_route_type_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced),
-        .rtm_flags   = obj->ip_route.r_rtm_flags & ((unsigned) (RTNH_F_ONLINK)),
-        .rtm_dst_len = obj->ip_route.plen,
-        .rtm_src_len = is_v4 ? 0 : NMP_OBJECT_CAST_IP6_ROUTE(obj)->src_plen,
-    };
-
-    gsize addr_len;
-
-    nm_assert(
-        NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
-    nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE));
-
-    msg = nlmsg_alloc_simple(nlmsg_type, (int) nlmsgflags);
-
-    if (nlmsg_append_struct(msg, &rtmsg) < 0)
-        goto nla_put_failure;
-
-    addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr);
-
-    NLA_PUT(msg,
-            RTA_DST,
-            addr_len,
-            is_v4 ? (gconstpointer) &obj->ip4_route.network
-                  : (gconstpointer) &obj->ip6_route.network);
-
-    if (!is_v4) {
-        if (!IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(obj)->src))
-            NLA_PUT(msg, RTA_SRC, addr_len, &obj->ip6_route.src);
-    }
-
-    NLA_PUT_U32(msg,
-                RTA_PRIORITY,
-                is_v4 ? nm_platform_ip4_route_get_effective_metric(&obj->ip4_route)
-                      : nm_platform_ip6_route_get_effective_metric(&obj->ip6_route));
-
-    if (table > 0xFF)
-        NLA_PUT_U32(msg, RTA_TABLE, table);
-
-    if (is_v4) {
-        if (NMP_OBJECT_CAST_IP4_ROUTE(obj)->pref_src)
-            NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src);
-    } else {
-        if (!IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(obj)->pref_src))
-            NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src);
-    }
-
-    if (obj->ip_route.mss || obj->ip_route.window || obj->ip_route.cwnd || obj->ip_route.initcwnd
-        || obj->ip_route.initrwnd || obj->ip_route.mtu || lock) {
-        struct nlattr *metrics;
-
-        metrics = nla_nest_start(msg, RTA_METRICS);
-        if (!metrics)
-            goto nla_put_failure;
-
-        if (obj->ip_route.mss)
-            NLA_PUT_U32(msg, RTAX_ADVMSS, obj->ip_route.mss);
-        if (obj->ip_route.window)
-            NLA_PUT_U32(msg, RTAX_WINDOW, obj->ip_route.window);
-        if (obj->ip_route.cwnd)
-            NLA_PUT_U32(msg, RTAX_CWND, obj->ip_route.cwnd);
-        if (obj->ip_route.initcwnd)
-            NLA_PUT_U32(msg, RTAX_INITCWND, obj->ip_route.initcwnd);
-        if (obj->ip_route.initrwnd)
-            NLA_PUT_U32(msg, RTAX_INITRWND, obj->ip_route.initrwnd);
-        if (obj->ip_route.mtu)
-            NLA_PUT_U32(msg, RTAX_MTU, obj->ip_route.mtu);
-        if (lock)
-            NLA_PUT_U32(msg, RTAX_LOCK, lock);
-
-        nla_nest_end(msg, metrics);
-    }
-
-    /* We currently don't have need for multi-hop routes... */
-    if (is_v4) {
-        NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway);
-    } else {
-        if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway))
-            NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway);
-    }
-    NLA_PUT_U32(msg, RTA_OIF, obj->ip_route.ifindex);
-
-    if (!is_v4 && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM)
-        NLA_PUT_U8(msg, RTA_PREF, obj->ip6_route.rt_pref);
-
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static struct nl_msg *
-_nl_msg_new_routing_rule(int nlmsg_type, int nlmsg_flags, const NMPlatformRoutingRule *routing_rule)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    const guint8 addr_size           = nm_utils_addr_family_to_size(routing_rule->addr_family);
-    guint32      table;
-
-    msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags);
-
-    table = routing_rule->table;
-
-    if (NM_IN_SET(routing_rule->addr_family, AF_INET, AF_INET6)
-        && routing_rule->action == FR_ACT_TO_TBL && routing_rule->l3mdev == 0
-        && table == RT_TABLE_UNSPEC) {
-        /* for IPv6, this setting is invalid and rejected by kernel. That's fine.
-         *
-         * for IPv4, kernel will automatically assign an unused table. That's not
-         * fine, because we don't know what we will get.
-         *
-         * The caller must not allow that to happen. */
-        nm_assert_not_reached();
-    }
-
-    {
-        const struct fib_rule_hdr frh = {
-            .family  = routing_rule->addr_family,
-            .src_len = routing_rule->src_len,
-            .dst_len = routing_rule->dst_len,
-            .tos     = routing_rule->tos,
-            .table   = table < 0x100u ? (guint8) table : (guint8) RT_TABLE_UNSPEC,
-            .action  = routing_rule->action,
-
-            /* we only allow setting the "not" flag. */
-            .flags = routing_rule->flags & ((guint32) FIB_RULE_INVERT),
-        };
-
-        if (nlmsg_append_struct(msg, &frh) < 0)
-            goto nla_put_failure;
-    }
-
-    if (table > G_MAXINT8)
-        NLA_PUT_U32(msg, FRA_TABLE, table);
-
-    if (routing_rule->suppress_prefixlen_inverse != 0)
-        NLA_PUT_U32(msg, FRA_SUPPRESS_PREFIXLEN, ~routing_rule->suppress_prefixlen_inverse);
-
-    if (routing_rule->suppress_ifgroup_inverse != 0)
-        NLA_PUT_U32(msg, FRA_SUPPRESS_IFGROUP, ~routing_rule->suppress_ifgroup_inverse);
-
-    if (routing_rule->iifname[0] != '\0')
-        NLA_PUT_STRING(msg, FRA_IIFNAME, routing_rule->iifname);
-
-    if (routing_rule->oifname[0] != '\0')
-        NLA_PUT_STRING(msg, FRA_OIFNAME, routing_rule->oifname);
-
-    /* we always set the priority and don't support letting kernel pick one. */
-    NLA_PUT_U32(msg, FRA_PRIORITY, routing_rule->priority);
-
-    if (routing_rule->fwmark != 0 || routing_rule->fwmask != 0) {
-        NLA_PUT_U32(msg, FRA_FWMARK, routing_rule->fwmark);
-        NLA_PUT_U32(msg, FRA_FWMASK, routing_rule->fwmask);
-    }
-
-    if (routing_rule->src_len > 0)
-        NLA_PUT(msg, FRA_SRC, addr_size, &routing_rule->src);
-
-    if (routing_rule->dst_len > 0)
-        NLA_PUT(msg, FRA_DST, addr_size, &routing_rule->dst);
-
-    if (routing_rule->flow != 0) {
-        /* only relevant for IPv4. */
-        NLA_PUT_U32(msg, FRA_FLOW, routing_rule->flow);
-    }
-
-    if (routing_rule->tun_id != 0)
-        NLA_PUT_U64(msg, FRA_TUN_ID, htobe64(routing_rule->tun_id));
-
-    if (routing_rule->l3mdev)
-        NLA_PUT_U8(msg, FRA_L3MDEV, routing_rule->l3mdev);
-
-    if (routing_rule->protocol != RTPROT_UNSPEC)
-        NLA_PUT_U8(msg, FRA_PROTOCOL, routing_rule->protocol);
-
-    if (routing_rule->ip_proto != 0)
-        NLA_PUT_U8(msg, FRA_IP_PROTO, routing_rule->ip_proto);
-
-    if (routing_rule->sport_range.start || routing_rule->sport_range.end)
-        NLA_PUT(msg,
-                FRA_SPORT_RANGE,
-                sizeof(routing_rule->sport_range),
-                &routing_rule->sport_range);
-
-    if (routing_rule->dport_range.start || routing_rule->dport_range.end)
-        NLA_PUT(msg,
-                FRA_DPORT_RANGE,
-                sizeof(routing_rule->dport_range),
-                &routing_rule->dport_range);
-
-    if (routing_rule->uid_range_has)
-        NLA_PUT(msg, FRA_UID_RANGE, sizeof(routing_rule->uid_range), &routing_rule->uid_range);
-
-    switch (routing_rule->action) {
-    case FR_ACT_GOTO:
-        NLA_PUT_U32(msg, FRA_GOTO, routing_rule->goto_target);
-        break;
-    }
-
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static struct nl_msg *
-_nl_msg_new_qdisc(int nlmsg_type, int nlmsg_flags, const NMPlatformQdisc *qdisc)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    struct nlattr *              tc_options;
-    const struct tcmsg           tcm = {
-        .tcm_family  = qdisc->addr_family,
-        .tcm_ifindex = qdisc->ifindex,
-        .tcm_handle  = qdisc->handle,
-        .tcm_parent  = qdisc->parent,
-        .tcm_info    = qdisc->info,
-    };
-
-    msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO);
-
-    if (nlmsg_append_struct(msg, &tcm) < 0)
-        goto nla_put_failure;
-
-    NLA_PUT_STRING(msg, TCA_KIND, qdisc->kind);
-
-    if (nm_streq(qdisc->kind, "sfq")) {
-        struct tc_sfq_qopt_v1 opt = {};
-
-        opt.v0.quantum        = qdisc->sfq.quantum;
-        opt.v0.limit          = qdisc->sfq.limit;
-        opt.v0.perturb_period = qdisc->sfq.perturb_period;
-        opt.v0.flows          = qdisc->sfq.flows;
-        opt.v0.divisor        = qdisc->sfq.divisor;
-        opt.depth             = qdisc->sfq.depth;
-
-        NLA_PUT(msg, TCA_OPTIONS, sizeof(opt), &opt);
-    } else if (nm_streq(qdisc->kind, "tbf")) {
-        struct tc_tbf_qopt opt = {};
-
-        if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS)))
-            goto nla_put_failure;
-
-        opt.rate.rate = (qdisc->tbf.rate >= (1ULL << 32)) ? ~0U : (guint32) qdisc->tbf.rate;
-        if (qdisc->tbf.limit)
-            opt.limit = qdisc->tbf.limit;
-        else if (qdisc->tbf.latency) {
-            opt.limit = qdisc->tbf.rate * (double) qdisc->tbf.latency / PSCHED_TIME_UNITS_PER_SEC
-                        + qdisc->tbf.burst;
-        }
-
-        NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opt), &opt);
-        if (qdisc->tbf.rate >= (1ULL << 32))
-            NLA_PUT_U64(msg, TCA_TBF_RATE64, qdisc->tbf.rate);
-        NLA_PUT_U32(msg, TCA_TBF_BURST, qdisc->tbf.burst);
-
-        nla_nest_end(msg, tc_options);
-    } else if (nm_streq(qdisc->kind, "prio")) {
-        struct tc_prio_qopt opt = {3, {1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}};
-
-        NLA_PUT(msg, TCA_OPTIONS, sizeof(opt), &opt);
-    } else {
-        if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS)))
-            goto nla_put_failure;
-
-        if (nm_streq(qdisc->kind, "fq_codel")) {
-            if (qdisc->fq_codel.limit)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit);
-            if (qdisc->fq_codel.flows)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows);
-            if (qdisc->fq_codel.target)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target);
-            if (qdisc->fq_codel.interval)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval);
-            if (qdisc->fq_codel.quantum)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum);
-            if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold);
-            if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit);
-            if (qdisc->fq_codel.ecn)
-                NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn);
-        }
-
-        nla_nest_end(msg, tc_options);
-    }
-
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static struct nl_msg *
-_nl_msg_new_tfilter(int nlmsg_type, int nlmsg_flags, const NMPlatformTfilter *tfilter)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    struct nlattr *              tc_options;
-    struct nlattr *              act_tab;
-    const struct tcmsg           tcm = {
-        .tcm_family  = tfilter->addr_family,
-        .tcm_ifindex = tfilter->ifindex,
-        .tcm_handle  = tfilter->handle,
-        .tcm_parent  = tfilter->parent,
-        .tcm_info    = tfilter->info,
-    };
-
-    msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO);
-
-    if (nlmsg_append_struct(msg, &tcm) < 0)
-        goto nla_put_failure;
-
-    NLA_PUT_STRING(msg, TCA_KIND, tfilter->kind);
-
-    if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS)))
-        goto nla_put_failure;
-
-    if (!(act_tab = nla_nest_start(msg, TCA_OPTIONS)))  // 3 TCA_ACT_KIND TCA_ACT_KIND
-        goto nla_put_failure;
-
-    if (tfilter->action.kind) {
-        const NMPlatformAction *action = &tfilter->action;
-        struct nlattr *         prio;
-        struct nlattr *         act_options;
-
-        if (!(prio = nla_nest_start(msg, 1 /* priority */)))
-            goto nla_put_failure;
-
-        NLA_PUT_STRING(msg, TCA_ACT_KIND, action->kind);
-
-        if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) {
-            const NMPlatformActionSimple *simple = &action->simple;
-            struct tc_defact              sel    = {
-                0,
-            };
-
-            if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS)))
-                goto nla_put_failure;
-
-            NLA_PUT(msg, TCA_DEF_PARMS, sizeof(sel), &sel);
-            NLA_PUT(msg, TCA_DEF_DATA, sizeof(simple->sdata), simple->sdata);
-
-            nla_nest_end(msg, act_options);
-
-        } else if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) {
-            const NMPlatformActionMirred *mirred = &action->mirred;
-            struct tc_mirred              sel    = {
-                0,
-            };
-
-            if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS)))
-                goto nla_put_failure;
-
-            if (mirred->egress && mirred->redirect)
-                sel.eaction = TCA_EGRESS_REDIR;
-            else if (mirred->egress && mirred->mirror)
-                sel.eaction = TCA_EGRESS_MIRROR;
-            else if (mirred->ingress && mirred->redirect)
-                sel.eaction = TCA_INGRESS_REDIR;
-            else if (mirred->ingress && mirred->mirror)
-                sel.eaction = TCA_INGRESS_MIRROR;
-            sel.ifindex = mirred->ifindex;
-
-            NLA_PUT(msg, TCA_MIRRED_PARMS, sizeof(sel), &sel);
-
-            nla_nest_end(msg, act_options);
-        }
-
-        nla_nest_end(msg, prio);
-    }
-
-    nla_nest_end(msg, tc_options);
-
-    nla_nest_end(msg, act_tab);
-
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-/*****************************************************************************/
-
-static struct nl_sock *
-_genl_sock(NMLinuxPlatform *platform)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-
-    return priv->genl;
-}
-
-#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path)                                                 \
-    G_STMT_START                                                                                \
-    {                                                                                           \
-        const char *const _pathid = (pathid);                                                   \
-        const int         _dirfd  = (dirfd);                                                    \
-        const char *const _path   = (path);                                                     \
-                                                                                                \
-        nm_assert(_path &&_path[0]);                                                            \
-        g_assert(!strstr(_path, "/../"));                                                       \
-        if (_dirfd < 0) {                                                                       \
-            nm_assert(!_pathid);                                                                \
-            nm_assert(_path[0] == '/');                                                         \
-            nm_assert(g_str_has_prefix(_path, "/proc/sys/") || g_str_has_prefix(_path, "/sys/") \
-                      || g_str_has_prefix(_path, "/proc/net"));                                 \
-        } else {                                                                                \
-            nm_assert(_pathid &&_pathid[0] && _pathid[0] != '/');                               \
-            nm_assert(_path[0] != '/');                                                         \
-        }                                                                                       \
-    }                                                                                           \
-    G_STMT_END
-
-/*****************************************************************************/
-
-/* core sysctl-set functions can be called from a non-main thread.
- * Hence, we require locking from nm-logging. Indicate that by
- * setting NM_THREAD_SAFE_ON_MAIN_THREAD to zero. */
-#undef NM_THREAD_SAFE_ON_MAIN_THREAD
-#define NM_THREAD_SAFE_ON_MAIN_THREAD 0
-
-static void
-_log_dbg_sysctl_set_impl(NMPlatform *platform,
-                         const char *pathid,
-                         int         dirfd,
-                         const char *path,
-                         const char *value)
-{
-    GError *      error         = NULL;
-    gs_free char *contents      = NULL;
-    gs_free char *value_escaped = g_strescape(value, NULL);
-
-    if (!nm_utils_file_get_contents(dirfd,
-                                    path,
-                                    1 * 1024 * 1024,
-                                    NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-                                    &contents,
-                                    NULL,
-                                    NULL,
-                                    &error)) {
-        _LOGD("sysctl: setting '%s' to '%s' (current value cannot be read: %s)",
-              pathid ?: path,
-              value_escaped,
-              error->message);
-        g_clear_error(&error);
-        return;
-    }
-
-    g_strstrip(contents);
-    if (nm_streq(contents, value))
-        _LOGD("sysctl: setting '%s' to '%s' (current value is identical)",
-              pathid ?: path,
-              value_escaped);
-    else {
-        gs_free char *contents_escaped = g_strescape(contents, NULL);
-
-        _LOGD("sysctl: setting '%s' to '%s' (current value is '%s')",
-              pathid ?: path,
-              value_escaped,
-              contents_escaped);
-    }
-}
-
-#define _log_dbg_sysctl_set(platform, pathid, dirfd, path, value)           \
-    G_STMT_START                                                            \
-    {                                                                       \
-        if (_LOGD_ENABLED()) {                                              \
-            _log_dbg_sysctl_set_impl(platform, pathid, dirfd, path, value); \
-        }                                                                   \
-    }                                                                       \
-    G_STMT_END
-
-static gboolean
-sysctl_set_internal(NMPlatform *platform,
-                    const char *pathid,
-                    int         dirfd,
-                    const char *path,
-                    const char *value)
-{
-    int           fd, tries;
-    gssize        nwrote;
-    gssize        len;
-    char *        actual;
-    gs_free char *actual_free = NULL;
-    int           errsv;
-
-    if (dirfd < 0) {
-        pathid = path;
-
-        fd = open(path, O_WRONLY | O_TRUNC | O_CLOEXEC);
-        if (fd == -1) {
-            errsv = errno;
-            if (errsv == ENOENT) {
-                _LOGD("sysctl: failed to open '%s': (%d) %s",
-                      pathid,
-                      errsv,
-                      nm_strerror_native(errsv));
-            } else {
-                _LOGE("sysctl: failed to open '%s': (%d) %s",
-                      pathid,
-                      errsv,
-                      nm_strerror_native(errsv));
-            }
-            errno = errsv;
-            return FALSE;
-        }
-    } else {
-        fd = openat(dirfd, path, O_WRONLY | O_TRUNC | O_CLOEXEC);
-        if (fd == -1) {
-            errsv = errno;
-            if (errsv == ENOENT) {
-                _LOGD("sysctl: failed to openat '%s': (%d) %s",
-                      pathid,
-                      errsv,
-                      nm_strerror_native(errsv));
-            } else {
-                _LOGE("sysctl: failed to openat '%s': (%d) %s",
-                      pathid,
-                      errsv,
-                      nm_strerror_native(errsv));
-            }
-            errno = errsv;
-            return FALSE;
-        }
-    }
-
-    _log_dbg_sysctl_set(platform, pathid, dirfd, path, value);
-
-    /* Most sysfs and sysctl options don't care about a trailing LF, while some
-     * (like infiniband) do.  So always add the LF.  Also, neither sysfs nor
-     * sysctl support partial writes so the LF must be added to the string we're
-     * about to write.
-     */
-    len = strlen(value) + 1;
-    nm_assert(len > 0);
-    if (len > 512)
-        actual = actual_free = g_malloc(len + 1);
-    else
-        actual = g_alloca(len + 1);
-    memcpy(actual, value, len - 1);
-    actual[len - 1] = '\n';
-    actual[len]     = '\0';
-
-    /* Try to write the entire value three times if a partial write occurs */
-    errsv = 0;
-    for (tries = 0, nwrote = 0; tries < 3 && nwrote < len - 1; tries++) {
-        nwrote = write(fd, actual, len);
-        if (nwrote == -1) {
-            errsv = errno;
-            if (errsv == EINTR) {
-                _LOGD("sysctl: interrupted, will try again");
-                continue;
-            }
-            break;
-        }
-    }
-    if (nwrote == -1) {
-        NMLogLevel level = LOGL_ERR;
-
-        if (errsv == EEXIST) {
-            level = LOGL_DEBUG;
-        } else if (errsv == EINVAL
-                   && nm_utils_sysctl_ip_conf_is_path(AF_INET6, path, NULL, "mtu")) {
-            /* setting the MTU can fail under regular conditions. Suppress
-             * logging a warning. */
-            level = LOGL_DEBUG;
-        }
-
-        _NMLOG(level,
-               "sysctl: failed to set '%s' to '%s': (%d) %s",
-               path,
-               value,
-               errsv,
-               nm_strerror_native(errsv));
-    } else if (nwrote < len - 1) {
-        _LOGE("sysctl: failed to set '%s' to '%s' after three attempts", path, value);
-    }
-
-    if (nwrote < len - 1) {
-        if (nm_close(fd) != 0) {
-            if (errsv != 0)
-                errno = errsv;
-        } else if (errsv != 0)
-            errno = errsv;
-        else
-            errno = EIO;
-        return FALSE;
-    }
-    if (nm_close(fd) != 0) {
-        /* errno is already properly set. */
-        return FALSE;
-    }
-
-    /* success. errno is undefined (no need to set). */
-    return TRUE;
-}
-
-#undef NM_THREAD_SAFE_ON_MAIN_THREAD
-#define NM_THREAD_SAFE_ON_MAIN_THREAD 1
-
-/*****************************************************************************/
-
-static gboolean
-sysctl_set(NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-
-    g_return_val_if_fail(path, FALSE);
-    g_return_val_if_fail(value, FALSE);
-
-    ASSERT_SYSCTL_ARGS(pathid, dirfd, path);
-
-    if (dirfd < 0 && !nm_platform_netns_push(platform, &netns)) {
-        errno = ENETDOWN;
-        return FALSE;
-    }
-
-    return sysctl_set_internal(platform, pathid, dirfd, path, value);
-}
-
-typedef struct {
-    NMPlatform *            platform;
-    char *                  pathid;
-    int                     dirfd;
-    char *                  path;
-    char **                 values;
-    GCancellable *          cancellable;
-    NMPlatformAsyncCallback callback;
-    gpointer                callback_data;
-} SysctlAsyncInfo;
-
-static void
-sysctl_async_info_free(SysctlAsyncInfo *info)
-{
-    g_object_unref(info->platform);
-    g_free(info->pathid);
-    if (info->dirfd >= 0)
-        nm_close(info->dirfd);
-    g_free(info->path);
-    g_strfreev(info->values);
-    g_object_unref(info->cancellable);
-    g_slice_free(SysctlAsyncInfo, info);
-}
-
-static void
-sysctl_async_cb(GObject *object, GAsyncResult *res, gpointer user_data)
-{
-    NMPlatform *     platform;
-    GTask *          task = G_TASK(res);
-    SysctlAsyncInfo *info;
-    gs_free_error GError *error      = NULL;
-    gs_free char *        values_str = NULL;
-
-    info = g_task_get_task_data(task);
-
-    if (g_task_propagate_boolean(task, &error)) {
-        platform = info->platform;
-        _LOGD("sysctl: successfully set-async '%s' to values '%s'",
-              info->pathid ?: info->path,
-              (values_str = g_strjoinv(", ", info->values)));
-    }
-
-    if (info->callback)
-        info->callback(error, info->callback_data);
-}
-
-static void
-sysctl_async_thread_fn(GTask *       task,
-                       gpointer      source_object,
-                       gpointer      task_data,
-                       GCancellable *cancellable)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    SysctlAsyncInfo *           info  = task_data;
-    GError *                    error = NULL;
-    char **                     value;
-
-    if (g_task_return_error_if_cancelled(task))
-        return;
-
-    if (info->dirfd < 0 && !nm_platform_netns_push(info->platform, &netns)) {
-        g_set_error_literal(&error,
-                            NM_UTILS_ERROR,
-                            NM_UTILS_ERROR_UNKNOWN,
-                            "sysctl: failed changing namespace");
-        g_task_return_error(task, error);
-        return;
-    }
-
-    for (value = info->values; *value; value++) {
-        if (!sysctl_set_internal(info->platform, info->pathid, info->dirfd, info->path, *value)) {
-            g_set_error(&error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_UNKNOWN,
-                        "sysctl: failed setting '%s' to value '%s': %s",
-                        info->pathid ?: info->path,
-                        *value,
-                        nm_strerror_native(errno));
-            g_task_return_error(task, error);
-            return;
-        }
-        if (g_task_return_error_if_cancelled(task))
-            return;
-    }
-    g_task_return_boolean(task, TRUE);
-}
-
-static void
-sysctl_set_async_return_idle(gpointer user_data, GCancellable *cancellable)
-{
-    gs_unref_object NMPlatform *platform  = NULL;
-    gs_free_error GError *cancelled_error = NULL;
-    gs_free_error GError *  error         = NULL;
-    NMPlatformAsyncCallback callback;
-    gpointer                callback_data;
-
-    nm_utils_user_data_unpack(user_data, &platform, &callback, &callback_data, &error);
-    g_cancellable_set_error_if_cancelled(cancellable, &cancelled_error);
-    callback(cancelled_error ?: error, callback_data);
-}
-
-static void
-sysctl_set_async(NMPlatform *            platform,
-                 const char *            pathid,
-                 int                     dirfd,
-                 const char *            path,
-                 const char *const *     values,
-                 NMPlatformAsyncCallback callback,
-                 gpointer                data,
-                 GCancellable *          cancellable)
-{
-    SysctlAsyncInfo *info;
-    GTask *          task;
-    int              dirfd_dup, errsv;
-    gpointer         packed;
-    GError *         error = NULL;
-
-    g_return_if_fail(platform);
-    g_return_if_fail(path);
-    g_return_if_fail(values && values[0]);
-    g_return_if_fail(cancellable);
-    g_return_if_fail(!data || callback);
-
-    ASSERT_SYSCTL_ARGS(pathid, dirfd, path);
-
-    if (dirfd >= 0) {
-        dirfd_dup = fcntl(dirfd, F_DUPFD_CLOEXEC, 0);
-        if (dirfd_dup < 0) {
-            if (!callback)
-                return;
-            errsv = errno;
-            g_set_error(&error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_UNKNOWN,
-                        "sysctl: failure duplicating directory fd: %s",
-                        nm_strerror_native(errsv));
-            packed = nm_utils_user_data_pack(g_object_ref(platform), callback, data, error);
-            nm_utils_invoke_on_idle(cancellable, sysctl_set_async_return_idle, packed);
-            return;
-        }
-    } else
-        dirfd_dup = -1;
-
-    info                = g_slice_new0(SysctlAsyncInfo);
-    info->platform      = g_object_ref(platform);
-    info->pathid        = g_strdup(pathid);
-    info->dirfd         = dirfd_dup;
-    info->path          = g_strdup(path);
-    info->values        = g_strdupv((char **) values);
-    info->callback      = callback;
-    info->callback_data = data;
-    info->cancellable   = g_object_ref(cancellable);
-
-    task = g_task_new(platform, cancellable, sysctl_async_cb, NULL);
-    g_task_set_task_data(task, info, (GDestroyNotify) sysctl_async_info_free);
-    g_task_set_return_on_cancel(task, FALSE);
-    g_task_run_in_thread(task, sysctl_async_thread_fn);
-    g_object_unref(task);
-}
-
-static GSList *sysctl_clear_cache_list;
-
-void
-_nm_logging_clear_platform_logging_cache(void)
-{
-    while (sysctl_clear_cache_list) {
-        NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(sysctl_clear_cache_list->data);
-
-        sysctl_clear_cache_list =
-            g_slist_delete_link(sysctl_clear_cache_list, sysctl_clear_cache_list);
-
-        g_hash_table_destroy(priv->sysctl_get_prev_values);
-        priv->sysctl_get_prev_values = NULL;
-    }
-}
-
-typedef struct {
-    const char *path;
-    CList       lst;
-    char *      value;
-    char        path_data[];
-} SysctlCacheEntry;
-
-static void
-sysctl_cache_entry_free(SysctlCacheEntry *entry)
-{
-    c_list_unlink_stale(&entry->lst);
-    g_free(entry->value);
-    g_free(entry);
-}
-
-static void
-_log_dbg_sysctl_get_impl(NMPlatform *platform, const char *pathid, const char *contents)
-{
-    NMLinuxPlatformPrivate *priv  = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    SysctlCacheEntry *      entry = NULL;
-
-    if (!priv->sysctl_get_prev_values) {
-        sysctl_clear_cache_list = g_slist_prepend(sysctl_clear_cache_list, platform);
-        c_list_init(&priv->sysctl_list);
-        priv->sysctl_get_prev_values =
-            g_hash_table_new_full(nm_pstr_hash,
-                                  nm_pstr_equal,
-                                  (GDestroyNotify) sysctl_cache_entry_free,
-                                  NULL);
-    } else
-        entry = g_hash_table_lookup(priv->sysctl_get_prev_values, &pathid);
-
-    if (entry) {
-        if (!nm_streq(entry->value, contents)) {
-            gs_free char *contents_escaped   = g_strescape(contents, NULL);
-            gs_free char *prev_value_escaped = g_strescape(entry->value, NULL);
-
-            _LOGD("sysctl: reading '%s': '%s' (changed from '%s' on last read)",
-                  pathid,
-                  contents_escaped,
-                  prev_value_escaped);
-            g_free(entry->value);
-            entry->value = g_strdup(contents);
-        }
-        nm_c_list_move_front(&priv->sysctl_list, &entry->lst);
-    } else {
-        gs_free char *    contents_escaped = g_strescape(contents, NULL);
-        SysctlCacheEntry *old;
-        size_t            len;
-
-        len          = strlen(pathid);
-        entry        = g_malloc(sizeof(SysctlCacheEntry) + len + 1);
-        entry->value = g_strdup(contents);
-        entry->path  = entry->path_data;
-        memcpy(entry->path_data, pathid, len + 1);
-
-        /* Remove oldest entry when the cache becomes too big */
-        if (g_hash_table_size(priv->sysctl_get_prev_values) > 1000) {
-            old = c_list_last_entry(&priv->sysctl_list, SysctlCacheEntry, lst);
-            g_hash_table_remove(priv->sysctl_get_prev_values, old);
-        }
-
-        _LOGD("sysctl: reading '%s': '%s'", pathid, contents_escaped);
-
-        g_hash_table_add(priv->sysctl_get_prev_values, entry);
-        c_list_link_front(&priv->sysctl_list, &entry->lst);
-    }
-}
-
-#define _log_dbg_sysctl_get(platform, pathid, contents)           \
-    G_STMT_START                                                  \
-    {                                                             \
-        if (_LOGD_ENABLED())                                      \
-            _log_dbg_sysctl_get_impl(platform, pathid, contents); \
-    }                                                             \
-    G_STMT_END
-
-static char *
-sysctl_get(NMPlatform *platform, const char *pathid, int dirfd, const char *path)
-{
-    nm_auto_pop_netns NMPNetns *netns    = NULL;
-    GError *                    error    = NULL;
-    gs_free char *              contents = NULL;
-
-    ASSERT_SYSCTL_ARGS(pathid, dirfd, path);
-
-    if (dirfd < 0) {
-        if (!nm_platform_netns_push(platform, &netns)) {
-            errno = EBUSY;
-            return NULL;
-        }
-        pathid = path;
-    }
-
-    if (!nm_utils_file_get_contents(dirfd,
-                                    path,
-                                    1 * 1024 * 1024,
-                                    NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-                                    &contents,
-                                    NULL,
-                                    NULL,
-                                    &error)) {
-        NMLogLevel log_level = LOGL_ERR;
-        int        errsv     = EBUSY;
-
-        if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
-            errsv     = ENOENT;
-            log_level = LOGL_DEBUG;
-        } else if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NODEV)
-                   || g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) {
-            /* We assume FAILED means EOPNOTSUP and don't log a error message. */
-            log_level = LOGL_DEBUG;
-        }
-
-        _NMLOG(log_level, "error reading %s: %s", pathid, error->message);
-        g_clear_error(&error);
-        errno = errsv;
-        return NULL;
-    }
-
-    g_strstrip(contents);
-
-    _log_dbg_sysctl_get(platform, pathid, contents);
-
-    /* errno is left undefined (as we don't return NULL). */
-    return g_steal_pointer(&contents);
-}
-
-/*****************************************************************************/
-
-static void
-process_events(NMPlatform *platform)
-{
-    delayed_action_handle_all(platform, TRUE);
-}
-
-/*****************************************************************************/
-
-static const RefreshAllInfo *
-refresh_all_type_get_info(RefreshAllType refresh_all_type)
-{
-    static const RefreshAllInfo infos[] = {
-#define R(_refresh_all_type, _obj_type, _addr_family) \
-    [_refresh_all_type] = {                           \
-        .obj_type    = _obj_type,                     \
-        .addr_family = _addr_family,                  \
-    }
-        R(REFRESH_ALL_TYPE_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC),
-        R(REFRESH_ALL_TYPE_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC),
-        R(REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC),
-        R(REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC),
-        R(REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC),
-        R(REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET),
-        R(REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6),
-        R(REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC),
-        R(REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC),
-#undef R
-    };
-
-    nm_assert(_NM_INT_NOT_NEGATIVE(refresh_all_type));
-    nm_assert(refresh_all_type < G_N_ELEMENTS(infos));
-    nm_assert(nmp_class_from_type(infos[refresh_all_type].obj_type));
-
-    return &infos[refresh_all_type];
-}
-
-static NM_UTILS_LOOKUP_DEFINE(
-    delayed_action_type_to_refresh_all_type,
-    DelayedActionType,
-    RefreshAllType,
-    NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(0),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES,
-                         REFRESH_ALL_TYPE_IP4_ADDRESSES),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES,
-                         REFRESH_ALL_TYPE_IP6_ADDRESSES),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4,
-                         REFRESH_ALL_TYPE_ROUTING_RULES_IP4),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6,
-                         REFRESH_ALL_TYPE_ROUTING_RULES_IP6),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS),
-    NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS),
-    NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), );
-
-static DelayedActionType
-delayed_action_type_from_refresh_all_type(RefreshAllType refresh_all_type)
-{
-    DelayedActionType t;
-
-    nm_assert(refresh_all_type_get_info(refresh_all_type));
-
-    t = (((DelayedActionType) 1) << refresh_all_type);
-
-    nm_assert(refresh_all_type == delayed_action_type_to_refresh_all_type(t));
-
-    return t;
-}
-
-static RefreshAllType
-refresh_all_type_from_needle_object(const NMPObject *obj_needle)
-{
-    switch (NMP_OBJECT_GET_TYPE(obj_needle)) {
-    case NMP_OBJECT_TYPE_LINK:
-        return REFRESH_ALL_TYPE_LINKS;
-    case NMP_OBJECT_TYPE_IP4_ADDRESS:
-        return REFRESH_ALL_TYPE_IP4_ADDRESSES;
-    case NMP_OBJECT_TYPE_IP6_ADDRESS:
-        return REFRESH_ALL_TYPE_IP6_ADDRESSES;
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-        return REFRESH_ALL_TYPE_IP4_ROUTES;
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-        return REFRESH_ALL_TYPE_IP6_ROUTES;
-    case NMP_OBJECT_TYPE_QDISC:
-        return REFRESH_ALL_TYPE_QDISCS;
-    case NMP_OBJECT_TYPE_TFILTER:
-        return REFRESH_ALL_TYPE_TFILTERS;
-    case NMP_OBJECT_TYPE_ROUTING_RULE:
-        switch (NMP_OBJECT_CAST_ROUTING_RULE(obj_needle)->addr_family) {
-        case AF_INET:
-            return REFRESH_ALL_TYPE_ROUTING_RULES_IP4;
-        case AF_INET6:
-            return REFRESH_ALL_TYPE_ROUTING_RULES_IP6;
-        }
-        nm_assert_not_reached();
-        return 0;
-    default:
-        nm_assert_not_reached();
-        return 0;
-    }
-}
-
-static const NMPLookup *
-refresh_all_type_init_lookup(RefreshAllType refresh_all_type, NMPLookup *lookup)
-{
-    const RefreshAllInfo *refresh_all_info;
-
-    nm_assert(lookup);
-
-    refresh_all_info = refresh_all_type_get_info(refresh_all_type);
-
-    nm_assert(refresh_all_info);
-
-    if (NM_IN_SET(refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) {
-        return nmp_lookup_init_object_by_addr_family(lookup,
-                                                     refresh_all_info->obj_type,
-                                                     refresh_all_info->addr_family);
-    }
-
-    /* not yet implemented. */
-    nm_assert(refresh_all_info->addr_family == AF_UNSPEC);
-
-    return nmp_lookup_init_obj_type(lookup, refresh_all_info->obj_type);
-}
-
-static DelayedActionType
-delayed_action_refresh_from_needle_object(const NMPObject *obj_needle)
-{
-    return delayed_action_type_from_refresh_all_type(
-        refresh_all_type_from_needle_object(obj_needle));
-}
-
-static NM_UTILS_LOOKUP_STR_DEFINE(
-    delayed_action_to_string,
-    DelayedActionType,
-    NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES,
-                             "refresh-all-ip4-addresses"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES,
-                             "refresh-all-ip6-addresses"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4,
-                             "refresh-all-routing-rules-ip4"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6,
-                             "refresh-all-routing-rules-ip6"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"),
-    NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_NONE),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(__DELAYED_ACTION_TYPE_MAX), );
-
-static const char *
-delayed_action_to_string_full(DelayedActionType action_type,
-                              gpointer          user_data,
-                              char *            buf,
-                              gsize             buf_size)
-{
-    char *                                    buf0 = buf;
-    const DelayedActionWaitForNlResponseData *data;
-
-    nm_utils_strbuf_append_str(&buf, &buf_size, delayed_action_to_string(action_type));
-    switch (action_type) {
-    case DELAYED_ACTION_TYPE_MASTER_CONNECTED:
-        nm_utils_strbuf_append(&buf, &buf_size, " (master-ifindex %d)", GPOINTER_TO_INT(user_data));
-        break;
-    case DELAYED_ACTION_TYPE_REFRESH_LINK:
-        nm_utils_strbuf_append(&buf, &buf_size, " (ifindex %d)", GPOINTER_TO_INT(user_data));
-        break;
-    case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE:
-        data = user_data;
-
-        if (data) {
-            gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_nsec();
-            char   b[255];
-
-            nm_utils_strbuf_append(
-                &buf,
-                &buf_size,
-                " (seq %u, timeout in %s%" G_GINT64_FORMAT ".%09" G_GINT64_FORMAT
-                ", response-type %d%s%s)",
-                data->seq_number,
-                timeout < 0 ? "-" : "",
-                (timeout < 0 ? -timeout : timeout) / NM_UTILS_NSEC_PER_SEC,
-                (timeout < 0 ? -timeout : timeout) % NM_UTILS_NSEC_PER_SEC,
-                (int) data->response_type,
-                data->seq_result ? ", " : "",
-                data->seq_result
-                    ? wait_for_nl_response_to_string(data->seq_result, NULL, b, sizeof(b))
-                    : "");
-        } else
-            nm_utils_strbuf_append_str(&buf, &buf_size, " (any)");
-        break;
-    default:
-        nm_assert(!user_data);
-        break;
-    }
-    return buf0;
-}
-
-#define _LOGt_delayed_action(action_type, user_data, operation)                           \
-    G_STMT_START                                                                          \
-    {                                                                                     \
-        char _buf[255];                                                                   \
-                                                                                          \
-        _LOGt("delayed-action: %s %s",                                                    \
-              "" operation,                                                               \
-              delayed_action_to_string_full(action_type, user_data, _buf, sizeof(_buf))); \
-    }                                                                                     \
-    G_STMT_END
-
-/*****************************************************************************/
-
-static gboolean
-delayed_action_refresh_all_in_progress(NMPlatform *platform, DelayedActionType action_type)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    RefreshAllType          refresh_all_type;
-
-    nm_assert(nm_utils_is_power_of_two(action_type));
-    nm_assert(NM_FLAGS_ANY(action_type, DELAYED_ACTION_TYPE_REFRESH_ALL));
-    nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL));
-
-    if (NM_FLAGS_ANY(priv->delayed_action.flags, action_type))
-        return TRUE;
-
-    refresh_all_type = delayed_action_type_to_refresh_all_type(action_type);
-    return (priv->delayed_action.refresh_all_in_progress[refresh_all_type] > 0);
-}
-
-static void
-delayed_action_wait_for_nl_response_complete(NMPlatform *            platform,
-                                             guint                   idx,
-                                             WaitForNlResponseResult seq_result)
-{
-    NMLinuxPlatformPrivate *            priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    DelayedActionWaitForNlResponseData *data;
-
-    nm_assert(NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
-    nm_assert(idx < priv->delayed_action.list_wait_for_nl_response->len);
-    nm_assert(seq_result);
-
-    data = &g_array_index(priv->delayed_action.list_wait_for_nl_response,
-                          DelayedActionWaitForNlResponseData,
-                          idx);
-
-    _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete");
-
-    if (priv->delayed_action.list_wait_for_nl_response->len <= 1)
-        priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE;
-    if (data->out_seq_result)
-        *data->out_seq_result = seq_result;
-    switch (data->response_type) {
-    case DELAYED_ACTION_RESPONSE_TYPE_VOID:
-        break;
-    case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS:
-        if (data->response.out_refresh_all_in_progress) {
-            nm_assert(*data->response.out_refresh_all_in_progress > 0);
-            *data->response.out_refresh_all_in_progress -= 1;
-            data->response.out_refresh_all_in_progress = NULL;
-        }
-        break;
-    case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET:
-        if (data->response.out_route_get) {
-            nm_assert(!*data->response.out_route_get);
-            data->response.out_route_get = NULL;
-        }
-        break;
-    }
-
-    g_array_remove_index_fast(priv->delayed_action.list_wait_for_nl_response, idx);
-}
-
-static void
-delayed_action_wait_for_nl_response_complete_check(NMPlatform *            platform,
-                                                   WaitForNlResponseResult force_result,
-                                                   guint32 *               out_next_seq_number,
-                                                   gint64 *                out_next_timeout_abs_ns,
-                                                   gint64 *                p_now_ns)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    guint                   i;
-    guint32                 next_seq_number     = 0;
-    gint64                  next_timeout_abs_ns = 0;
-    int                     now_ns              = 0;
-
-    for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len;) {
-        const DelayedActionWaitForNlResponseData *data =
-            &g_array_index(priv->delayed_action.list_wait_for_nl_response,
-                           DelayedActionWaitForNlResponseData,
-                           i);
-
-        if (data->seq_result)
-            delayed_action_wait_for_nl_response_complete(platform, i, data->seq_result);
-        else if (p_now_ns
-                 && ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_nsec()))
-                     >= data->timeout_abs_ns)) {
-            /* the caller can optionally check for timeout by providing a p_now_ns argument. */
-            delayed_action_wait_for_nl_response_complete(
-                platform,
-                i,
-                WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT);
-        } else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN)
-            delayed_action_wait_for_nl_response_complete(platform, i, force_result);
-        else {
-            if (next_seq_number == 0 || next_timeout_abs_ns > data->timeout_abs_ns) {
-                next_seq_number     = data->seq_number;
-                next_timeout_abs_ns = data->timeout_abs_ns;
-            }
-            i++;
-        }
-    }
-
-    if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) {
-        nm_assert(
-            !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
-        nm_assert(priv->delayed_action.list_wait_for_nl_response->len == 0);
-    }
-
-    NM_SET_OUT(out_next_seq_number, next_seq_number);
-    NM_SET_OUT(out_next_timeout_abs_ns, next_timeout_abs_ns);
-    NM_SET_OUT(p_now_ns, now_ns);
-}
-
-static void
-delayed_action_wait_for_nl_response_complete_all(NMPlatform *            platform,
-                                                 WaitForNlResponseResult fallback_result)
-{
-    delayed_action_wait_for_nl_response_complete_check(platform, fallback_result, NULL, NULL, NULL);
-}
-
-/*****************************************************************************/
-
-static void
-delayed_action_handle_MASTER_CONNECTED(NMPlatform *platform, int master_ifindex)
-{
-    nm_auto_nmpobj const NMPObject *obj_old = NULL;
-    nm_auto_nmpobj const NMPObject *obj_new = NULL;
-    NMPCacheOpsType                 cache_op;
-
-    cache_op = nmp_cache_update_link_master_connected(nm_platform_get_cache(platform),
-                                                      master_ifindex,
-                                                      &obj_old,
-                                                      &obj_new);
-    if (cache_op == NMP_CACHE_OPS_UNCHANGED)
-        return;
-    cache_on_change(platform, cache_op, obj_old, obj_new);
-    nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new);
-}
-
-static void
-delayed_action_handle_REFRESH_LINK(NMPlatform *platform, int ifindex)
-{
-    do_request_link_no_delayed_actions(platform, ifindex, NULL);
-}
-
-static void
-delayed_action_handle_REFRESH_ALL(NMPlatform *platform, DelayedActionType flags)
-{
-    do_request_all_no_delayed_actions(platform, flags);
-}
-
-static void
-delayed_action_handle_READ_NETLINK(NMPlatform *platform)
-{
-    event_handler_read_netlink(platform, FALSE);
-}
-
-static void
-delayed_action_handle_WAIT_FOR_NL_RESPONSE(NMPlatform *platform)
-{
-    event_handler_read_netlink(platform, TRUE);
-}
-
-static gboolean
-delayed_action_handle_one(NMPlatform *platform)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    gpointer                user_data;
-
-    if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE)
-        return FALSE;
-
-    /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions.
-     * This type of action is entirely cache-internal and is here to resolve a
-     * cache inconsistency. It should be fixed right away. */
-    if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) {
-        nm_assert(priv->delayed_action.list_master_connected->len > 0);
-
-        user_data = priv->delayed_action.list_master_connected->pdata[0];
-        g_ptr_array_remove_index_fast(priv->delayed_action.list_master_connected, 0);
-        if (priv->delayed_action.list_master_connected->len == 0)
-            priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_MASTER_CONNECTED;
-        nm_assert(_nm_utils_ptrarray_find_first(
-                      (gconstpointer *) priv->delayed_action.list_master_connected->pdata,
-                      priv->delayed_action.list_master_connected->len,
-                      user_data)
-                  < 0);
-
-        _LOGt_delayed_action(DELAYED_ACTION_TYPE_MASTER_CONNECTED, user_data, "handle");
-        delayed_action_handle_MASTER_CONNECTED(platform, GPOINTER_TO_INT(user_data));
-        return TRUE;
-    }
-    nm_assert(priv->delayed_action.list_master_connected->len == 0);
-
-    /* Next we prefer read-netlink, because the buffer size is limited and we want to process events
-     * from netlink early. */
-    if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_READ_NETLINK)) {
-        _LOGt_delayed_action(DELAYED_ACTION_TYPE_READ_NETLINK, NULL, "handle");
-        priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_READ_NETLINK;
-        delayed_action_handle_READ_NETLINK(platform);
-        return TRUE;
-    }
-
-    if (NM_FLAGS_ANY(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) {
-        DelayedActionType flags, iflags;
-
-        flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL;
-
-        priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL;
-
-        if (_LOGt_ENABLED()) {
-            FOR_EACH_DELAYED_ACTION(iflags, flags)
-            _LOGt_delayed_action(iflags, NULL, "handle");
-        }
-
-        delayed_action_handle_REFRESH_ALL(platform, flags);
-        return TRUE;
-    }
-
-    if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) {
-        nm_assert(priv->delayed_action.list_refresh_link->len > 0);
-
-        user_data = priv->delayed_action.list_refresh_link->pdata[0];
-        g_ptr_array_remove_index_fast(priv->delayed_action.list_refresh_link, 0);
-        if (priv->delayed_action.list_refresh_link->len == 0)
-            priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK;
-        nm_assert(_nm_utils_ptrarray_find_first(
-                      (gconstpointer *) priv->delayed_action.list_refresh_link->pdata,
-                      priv->delayed_action.list_refresh_link->len,
-                      user_data)
-                  < 0);
-
-        _LOGt_delayed_action(DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle");
-
-        delayed_action_handle_REFRESH_LINK(platform, GPOINTER_TO_INT(user_data));
-
-        return TRUE;
-    }
-
-    if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
-        nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0);
-        _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, NULL, "handle");
-        delayed_action_handle_WAIT_FOR_NL_RESPONSE(platform);
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-static gboolean
-delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    gboolean                any  = FALSE;
-
-    g_return_val_if_fail(priv->delayed_action.is_handling == 0, FALSE);
-
-    priv->delayed_action.is_handling++;
-    if (read_netlink)
-        delayed_action_schedule(platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL);
-    while (delayed_action_handle_one(platform))
-        any = TRUE;
-    priv->delayed_action.is_handling--;
-
-    cache_prune_all(platform);
-
-    return any;
-}
-
-static void
-delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpointer user_data)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    DelayedActionType       iflags;
-
-    nm_assert(action_type != DELAYED_ACTION_TYPE_NONE);
-
-    switch (action_type) {
-    case DELAYED_ACTION_TYPE_REFRESH_LINK:
-        if (_nm_utils_ptrarray_find_first(
-                (gconstpointer *) priv->delayed_action.list_refresh_link->pdata,
-                priv->delayed_action.list_refresh_link->len,
-                user_data)
-            < 0)
-            g_ptr_array_add(priv->delayed_action.list_refresh_link, user_data);
-        break;
-    case DELAYED_ACTION_TYPE_MASTER_CONNECTED:
-        if (_nm_utils_ptrarray_find_first(
-                (gconstpointer *) priv->delayed_action.list_master_connected->pdata,
-                priv->delayed_action.list_master_connected->len,
-                user_data)
-            < 0)
-            g_ptr_array_add(priv->delayed_action.list_master_connected, user_data);
-        break;
-    case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE:
-        g_array_append_vals(priv->delayed_action.list_wait_for_nl_response, user_data, 1);
-        break;
-    default:
-        nm_assert(!user_data);
-        nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_REFRESH_LINK));
-        nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED));
-        nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
-        break;
-    }
-
-    priv->delayed_action.flags |= action_type;
-
-    if (_LOGt_ENABLED()) {
-        FOR_EACH_DELAYED_ACTION(iflags, action_type)
-        _LOGt_delayed_action(iflags, user_data, "schedule");
-    }
-}
-
-static void
-delayed_action_schedule_WAIT_FOR_NL_RESPONSE(NMPlatform *                       platform,
-                                             guint32                            seq_number,
-                                             WaitForNlResponseResult *          out_seq_result,
-                                             char **                            out_errmsg,
-                                             DelayedActionWaitForNlResponseType response_type,
-                                             gpointer                           response_out_data)
-{
-    DelayedActionWaitForNlResponseData data = {
-        .seq_number = seq_number,
-        .timeout_abs_ns =
-            nm_utils_get_monotonic_timestamp_nsec() + (200 * (NM_UTILS_NSEC_PER_SEC / 1000)),
-        .out_seq_result    = out_seq_result,
-        .out_errmsg        = out_errmsg,
-        .response_type     = response_type,
-        .response.out_data = response_out_data,
-    };
-
-    delayed_action_schedule(platform, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, &data);
-}
-
-/*****************************************************************************/
-
-static void
-cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup)
-{
-    NMDedupMultiIter iter;
-    const NMPObject *obj;
-    NMPCacheOpsType  cache_op;
-    NMPCache *       cache = nm_platform_get_cache(platform);
-
-    nm_dedup_multi_iter_init(&iter, nmp_cache_lookup(cache, lookup));
-    while (nm_dedup_multi_iter_next(&iter)) {
-        const NMDedupMultiEntry *main_entry;
-
-        /* we only track the dirty flag for the OBJECT-TYPE index. That means,
-         * for other lookup types we need to check the dirty flag of the main-entry. */
-        main_entry = nmp_cache_reresolve_main_entry(cache, iter.current, lookup);
-        if (!main_entry->dirty)
-            continue;
-
-        obj = main_entry->obj;
-
-        _LOGt("cache-prune: prune %s",
-              nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
-
-        {
-            nm_auto_nmpobj const NMPObject *obj_old = NULL;
-
-            cache_op = nmp_cache_remove(cache, obj, TRUE, TRUE, &obj_old);
-            nm_assert(cache_op == NMP_CACHE_OPS_REMOVED);
-            cache_on_change(platform, cache_op, obj_old, NULL);
-            nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, NULL);
-        }
-    }
-}
-
-static void
-cache_prune_all(NMPlatform *platform)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    RefreshAllType          refresh_all_type;
-
-    for (refresh_all_type = _REFRESH_ALL_TYPE_FIRST; refresh_all_type < _REFRESH_ALL_TYPE_NUM;
-         refresh_all_type++) {
-        NMPLookup lookup;
-
-        if (priv->pruning[refresh_all_type] == 0)
-            continue;
-        priv->pruning[refresh_all_type] -= 1;
-        if (priv->pruning[refresh_all_type] > 0)
-            continue;
-        refresh_all_type_init_lookup(refresh_all_type, &lookup);
-        cache_prune_one_type(platform, &lookup);
-    }
-}
-
-static void
-cache_on_change(NMPlatform *     platform,
-                NMPCacheOpsType  cache_op,
-                const NMPObject *obj_old,
-                const NMPObject *obj_new)
-{
-    const NMPClass *klass;
-    char            str_buf[sizeof(_nm_utils_to_string_buffer)];
-    char            str_buf2[sizeof(_nm_utils_to_string_buffer)];
-    NMPCache *      cache = nm_platform_get_cache(platform);
-
-    ASSERT_nmp_cache_ops(cache, cache_op, obj_old, obj_new);
-    nm_assert(cache_op != NMP_CACHE_OPS_UNCHANGED);
-
-    klass = obj_old ? NMP_OBJECT_GET_CLASS(obj_old) : NMP_OBJECT_GET_CLASS(obj_new);
-
-    _LOGt(
-        "update-cache-%s: %s: %s%s%s",
-        klass->obj_type_name,
-        (cache_op == NMP_CACHE_OPS_UPDATED ? "UPDATE"
-                                           : (cache_op == NMP_CACHE_OPS_REMOVED   ? "REMOVE"
-                                              : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD"
-                                                                                  : "???")),
-        (cache_op != NMP_CACHE_OPS_ADDED
-             ? nmp_object_to_string(obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof(str_buf2))
-             : nmp_object_to_string(obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof(str_buf2))),
-        (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "",
-        (cache_op == NMP_CACHE_OPS_UPDATED
-             ? nmp_object_to_string(obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof(str_buf))
-             : ""));
-
-    switch (klass->obj_type) {
-    case NMP_OBJECT_TYPE_LINK:
-    {
-        /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */
-        if (obj_old
-            && nmp_cache_link_connected_needs_toggle_by_ifindex(cache,
-                                                                obj_old->link.master,
-                                                                obj_new,
-                                                                obj_old))
-            delayed_action_schedule(platform,
-                                    DELAYED_ACTION_TYPE_MASTER_CONNECTED,
-                                    GINT_TO_POINTER(obj_old->link.master));
-        if (obj_new && (!obj_old || obj_old->link.master != obj_new->link.master)
-            && nmp_cache_link_connected_needs_toggle_by_ifindex(cache,
-                                                                obj_new->link.master,
-                                                                obj_new,
-                                                                obj_old))
-            delayed_action_schedule(platform,
-                                    DELAYED_ACTION_TYPE_MASTER_CONNECTED,
-                                    GINT_TO_POINTER(obj_new->link.master));
-    }
-        {
-            /* check whether we are about to change a master link that needs toggling connected state. */
-            if (obj_new /* <-- nonsensical, make coverity happy */
-                && nmp_cache_link_connected_needs_toggle(cache, obj_new, obj_new, obj_old))
-                delayed_action_schedule(platform,
-                                        DELAYED_ACTION_TYPE_MASTER_CONNECTED,
-                                        GINT_TO_POINTER(obj_new->link.ifindex));
-        }
-        {
-            int ifindex = 0;
-
-            /* if we remove a link (from netlink), we must refresh the addresses, routes, qdiscs and tfilters */
-            if (cache_op == NMP_CACHE_OPS_REMOVED
-                && obj_old /* <-- nonsensical, make coverity happy */)
-                ifindex = obj_old->link.ifindex;
-            else if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old
-                     && obj_new /* <-- nonsensical, make coverity happy */
-                     && !obj_new->_link.netlink.is_in_netlink
-                     && obj_new->_link.netlink.is_in_netlink
-                            != obj_old->_link.netlink.is_in_netlink)
-                ifindex = obj_new->link.ifindex;
-
-            if (ifindex > 0) {
-                delayed_action_schedule(platform,
-                                        DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES
-                                            | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES
-                                            | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
-                                            | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES
-                                            | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL
-                                            | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS
-                                            | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
-                                        NULL);
-            }
-        }
-        {
-            int ifindex = -1;
-
-            /* removal of a link could be caused by moving the link to another netns.
-             * In this case, we potentially have to update other links that have this link as parent.
-             * Currently, kernel misses to sent us a notification in this case
-             * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */
-
-            if (cache_op == NMP_CACHE_OPS_REMOVED
-                && obj_old /* <-- nonsensical, make coverity happy */
-                && obj_old->_link.netlink.is_in_netlink)
-                ifindex = obj_old->link.ifindex;
-            else if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old
-                     && obj_new /* <-- nonsensical, make coverity happy */
-                     && obj_old->_link.netlink.is_in_netlink
-                     && !obj_new->_link.netlink.is_in_netlink)
-                ifindex = obj_new->link.ifindex;
-
-            if (ifindex > 0) {
-                NMPLookup             lookup;
-                NMDedupMultiIter      iter;
-                const NMPlatformLink *l;
-
-                nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK);
-                nmp_cache_iter_for_each_link (&iter, nmp_cache_lookup(cache, &lookup), &l) {
-                    if (l->parent == ifindex)
-                        delayed_action_schedule(platform,
-                                                DELAYED_ACTION_TYPE_REFRESH_LINK,
-                                                GINT_TO_POINTER(l->ifindex));
-                }
-            }
-        }
-        {
-            /* if a link goes down, we must refresh routes */
-            if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old
-                && obj_new /* <-- nonsensical, make coverity happy */
-                && obj_old->_link.netlink.is_in_netlink && obj_new->_link.netlink.is_in_netlink
-                && ((NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_UP)
-                     && !NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_UP))
-                    || (NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_LOWER_UP)
-                        && !NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) {
-                /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I
-                 * think kernel does send RTM_DELROUTE events for IPv6 routes, so
-                 * we might not need to refresh IPv6 routes. */
-                delayed_action_schedule(platform,
-                                        DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
-                                            | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
-                                        NULL);
-            }
-        }
-        if (NM_IN_SET(cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)
-            && (obj_new && obj_new->_link.netlink.is_in_netlink)
-            && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) {
-            gboolean                re_request_link = FALSE;
-            const NMPlatformLnkTun *lnk_tun;
-
-            if (!obj_new->_link.netlink.lnk
-                && NM_IN_SET(obj_new->link.type,
-                             NM_LINK_TYPE_GRE,
-                             NM_LINK_TYPE_GRETAP,
-                             NM_LINK_TYPE_IP6TNL,
-                             NM_LINK_TYPE_IP6GRE,
-                             NM_LINK_TYPE_IP6GRETAP,
-                             NM_LINK_TYPE_INFINIBAND,
-                             NM_LINK_TYPE_MACVLAN,
-                             NM_LINK_TYPE_MACVLAN,
-                             NM_LINK_TYPE_SIT,
-                             NM_LINK_TYPE_TUN,
-                             NM_LINK_TYPE_VLAN,
-                             NM_LINK_TYPE_VXLAN)) {
-                /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that
-                 * kernel didn't send this notification, thus when we first learn about a link
-                 * that lacks an lnk_data we re-request it again.
-                 *
-                 * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */
-                re_request_link = TRUE;
-            } else if (obj_new->link.type == NM_LINK_TYPE_TUN && obj_new->_link.netlink.lnk
-                       && (lnk_tun = &(obj_new->_link.netlink.lnk)->lnk_tun) && !lnk_tun->persist
-                       && lnk_tun->pi && !lnk_tun->vnet_hdr && !lnk_tun->multi_queue
-                       && !lnk_tun->owner_valid && !lnk_tun->group_valid) {
-                /* kernel has/had a know issue that the first notification for TUN device would
-                 * be sent with invalid parameters. The message looks like that kind, so refetch
-                 * it. */
-                re_request_link = TRUE;
-            } else if (obj_new->link.type == NM_LINK_TYPE_VETH && obj_new->link.parent == 0) {
-                /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK
-                 * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827).
-                 * Request it again. */
-                re_request_link = TRUE;
-            } else if (obj_new->link.type == NM_LINK_TYPE_ETHERNET
-                       && obj_new->link.l_address.len == 0) {
-                /* Due to a kernel bug, we sometimes receive spurious NEWLINK
-                 * messages after a wifi interface has disappeared. Since the
-                 * link is not present anymore we can't determine its type and
-                 * thus it will show up as a Ethernet one, with no address
-                 * specified.  Request the link again to check if it really
-                 * exists.  https://bugzilla.redhat.com/show_bug.cgi?id=1302037
-                 */
-                re_request_link = TRUE;
-            }
-            if (re_request_link) {
-                delayed_action_schedule(platform,
-                                        DELAYED_ACTION_TYPE_REFRESH_LINK,
-                                        GINT_TO_POINTER(obj_new->link.ifindex));
-            }
-        }
-        {
-            /* on enslave/release, we also refresh the master. */
-            int      ifindex1 = 0, ifindex2 = 0;
-            gboolean changed_master, changed_connected;
-
-            changed_master =
-                (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0
-                     ? obj_new->link.master
-                     : 0)
-                != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0
-                        ? obj_old->link.master
-                        : 0);
-            changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink
-                                     ? NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_LOWER_UP)
-                                     : 2)
-                                != (obj_old && obj_old->_link.netlink.is_in_netlink
-                                        ? NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_LOWER_UP)
-                                        : 2);
-
-            if (changed_master || changed_connected) {
-                ifindex1 =
-                    (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0)
-                        ? obj_old->link.master
-                        : 0;
-                ifindex2 =
-                    (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0)
-                        ? obj_new->link.master
-                        : 0;
-
-                if (ifindex1 > 0)
-                    delayed_action_schedule(platform,
-                                            DELAYED_ACTION_TYPE_REFRESH_LINK,
-                                            GINT_TO_POINTER(ifindex1));
-                if (ifindex2 > 0 && ifindex1 != ifindex2)
-                    delayed_action_schedule(platform,
-                                            DELAYED_ACTION_TYPE_REFRESH_LINK,
-                                            GINT_TO_POINTER(ifindex2));
-            }
-        }
-        break;
-    case NMP_OBJECT_TYPE_IP4_ADDRESS:
-    case NMP_OBJECT_TYPE_IP6_ADDRESS:
-    {
-        /* Address deletion is sometimes accompanied by route deletion. We need to
-             * check all routes belonging to the same interface. */
-        if (cache_op == NMP_CACHE_OPS_REMOVED) {
-            delayed_action_schedule(platform,
-                                    (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS)
-                                        ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
-                                        : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
-                                    NULL);
-        }
-    } break;
-    default:
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static guint32
-_nlh_seq_next_get(NMLinuxPlatformPrivate *priv)
-{
-    /* generate a new sequence number, but never return zero.
-     * Wrapping numbers are not a problem, because we don't rely
-     * on strictly increasing sequence numbers. */
-    return (++priv->nlh_seq_next) ?: (++priv->nlh_seq_next);
-}
-
-/**
- * _nl_send_nlmsghdr:
- * @platform:
- * @nlhdr:
- * @out_seq_result:
- * @response_type:
- * @response_out_data:
- *
- * Returns: 0 on success or a negative errno.
- */
-static int
-_nl_send_nlmsghdr(NMPlatform *                       platform,
-                  struct nlmsghdr *                  nlhdr,
-                  WaitForNlResponseResult *          out_seq_result,
-                  char **                            out_errmsg,
-                  DelayedActionWaitForNlResponseType response_type,
-                  gpointer                           response_out_data)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    guint32                 seq;
-    int                     errsv;
-
-    nm_assert(nlhdr);
-
-    seq              = _nlh_seq_next_get(priv);
-    nlhdr->nlmsg_seq = seq;
-
-    {
-        struct sockaddr_nl nladdr = {
-            .nl_family = AF_NETLINK,
-        };
-        struct iovec  iov = {.iov_base = nlhdr, .iov_len = nlhdr->nlmsg_len};
-        struct msghdr msg = {
-            .msg_name    = &nladdr,
-            .msg_namelen = sizeof(nladdr),
-            .msg_iov     = &iov,
-            .msg_iovlen  = 1,
-        };
-        int try_count;
-
-        if (!nlhdr->nlmsg_pid)
-            nlhdr->nlmsg_pid = nl_socket_get_local_port(priv->nlh);
-        nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK);
-
-        try_count = 0;
-again:
-        errsv = sendmsg(nl_socket_get_fd(priv->nlh), &msg, 0);
-        if (errsv < 0) {
-            errsv = errno;
-            if (errsv == EINTR && try_count++ < 100)
-                goto again;
-            _LOGD("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)",
-                  nm_strerror_native(errsv),
-                  errsv);
-            return -nm_errno_from_native(errsv);
-        }
-    }
-
-    delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform,
-                                                 seq,
-                                                 out_seq_result,
-                                                 out_errmsg,
-                                                 response_type,
-                                                 response_out_data);
-    return 0;
-}
-
-/**
- * _nl_send_nlmsg:
- * @platform:
- * @nlmsg:
- * @out_seq_result:
- * @response_type:
- * @response_out_data:
- *
- * Returns: 0 on success, or a negative libnl3 error code (beware, it's not an errno).
- */
-static int
-_nl_send_nlmsg(NMPlatform *                       platform,
-               struct nl_msg *                    nlmsg,
-               WaitForNlResponseResult *          out_seq_result,
-               char **                            out_errmsg,
-               DelayedActionWaitForNlResponseType response_type,
-               gpointer                           response_out_data)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    struct nlmsghdr *       nlhdr;
-    guint32                 seq;
-    int                     nle;
-
-    nlhdr            = nlmsg_hdr(nlmsg);
-    seq              = _nlh_seq_next_get(priv);
-    nlhdr->nlmsg_seq = seq;
-
-    nle = nl_send_auto(priv->nlh, nlmsg);
-    if (nle < 0) {
-        _LOGD("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nm_strerror(nle), nle);
-        return nle;
-    }
-
-    delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform,
-                                                 seq,
-                                                 out_seq_result,
-                                                 out_errmsg,
-                                                 response_type,
-                                                 response_out_data);
-    return 0;
-}
-
-static void
-do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name)
-{
-    NMLinuxPlatformPrivate *     priv  = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    int                          nle;
-
-    if (name && !name[0])
-        name = NULL;
-
-    g_return_if_fail(ifindex > 0 || name);
-
-    _LOGD("do-request-link: %d %s", ifindex, name ?: "");
-
-    if (ifindex > 0) {
-        const NMDedupMultiEntry *entry;
-
-        entry = nmp_cache_lookup_entry_link(nm_platform_get_cache(platform), ifindex);
-        if (entry) {
-            priv->pruning[REFRESH_ALL_TYPE_LINKS] += 1;
-            nm_dedup_multi_entry_set_dirty(entry, TRUE);
-        }
-    }
-
-    event_handler_read_netlink(platform, FALSE);
-
-    nlmsg = _nl_msg_new_link(RTM_GETLINK, 0, ifindex, name);
-    if (nlmsg) {
-        nle = _nl_send_nlmsg(platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
-        if (nle < 0) {
-            _LOGE("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)",
-                  ifindex,
-                  name ?: "",
-                  nm_strerror(nle),
-                  -nle);
-            return;
-        }
-    }
-}
-
-static void
-do_request_link(NMPlatform *platform, int ifindex, const char *name)
-{
-    do_request_link_no_delayed_actions(platform, ifindex, name);
-    delayed_action_handle_all(platform, FALSE);
-}
-
-static struct nl_msg *
-_nl_msg_new_dump(NMPObjectType obj_type, int preferred_addr_family)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    const NMPClass *             klass;
-
-    klass = nmp_class_from_type(obj_type);
-
-    nm_assert(klass);
-    nm_assert(klass->rtm_gettype > 0);
-
-    nlmsg = nlmsg_alloc_simple(klass->rtm_gettype, NLM_F_DUMP);
-
-    if (klass->addr_family != AF_UNSPEC) {
-        /* if the class specifies a particular address family, then it is preferred. */
-        nm_assert(NM_IN_SET(preferred_addr_family, AF_UNSPEC, klass->addr_family));
-        preferred_addr_family = klass->addr_family;
-    }
-
-    switch (klass->obj_type) {
-    case NMP_OBJECT_TYPE_QDISC:
-    case NMP_OBJECT_TYPE_TFILTER:
-    {
-        const struct tcmsg tcmsg = {
-            .tcm_family = preferred_addr_family,
-        };
-
-        if (nlmsg_append_struct(nlmsg, &tcmsg) < 0)
-            g_return_val_if_reached(NULL);
-    } break;
-    case NMP_OBJECT_TYPE_LINK:
-    case NMP_OBJECT_TYPE_IP4_ADDRESS:
-    case NMP_OBJECT_TYPE_IP6_ADDRESS:
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-    case NMP_OBJECT_TYPE_ROUTING_RULE:
-    {
-        const struct rtgenmsg gmsg = {
-            .rtgen_family = preferred_addr_family,
-        };
-
-        if (nlmsg_append_struct(nlmsg, &gmsg) < 0)
-            g_return_val_if_reached(NULL);
-    } break;
-    default:
-        g_return_val_if_reached(NULL);
-    }
-
-    return g_steal_pointer(&nlmsg);
-}
-
-static void
-do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    DelayedActionType       action_type_prune;
-    DelayedActionType       iflags;
-
-    nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL));
-    action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL;
-
-    action_type_prune = action_type;
-
-    /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra
-     * cache lookup for every entry.
-     *
-     * Avoid that, by special casing routing-rules here. */
-    if (NM_FLAGS_ALL(action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) {
-        NMPLookup lookup;
-
-        priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1;
-        priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1;
-        nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE);
-        nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup);
-        action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL;
-    }
-
-    FOR_EACH_DELAYED_ACTION(iflags, action_type_prune)
-    {
-        RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type(iflags);
-        NMPLookup      lookup;
-
-        priv->pruning[refresh_all_type] += 1;
-        refresh_all_type_init_lookup(refresh_all_type, &lookup);
-        nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup);
-    }
-
-    FOR_EACH_DELAYED_ACTION(iflags, action_type)
-    {
-        RefreshAllType        refresh_all_type = delayed_action_type_to_refresh_all_type(iflags);
-        const RefreshAllInfo *refresh_all_info = refresh_all_type_get_info(refresh_all_type);
-        nm_auto_nlmsg struct nl_msg *nlmsg     = NULL;
-        int *                        out_refresh_all_in_progress;
-
-        out_refresh_all_in_progress =
-            &priv->delayed_action.refresh_all_in_progress[refresh_all_type];
-        nm_assert(*out_refresh_all_in_progress >= 0);
-        *out_refresh_all_in_progress += 1;
-
-        /* clear any delayed action that request a refresh of this object type. */
-        priv->delayed_action.flags &= ~iflags;
-        _LOGt_delayed_action(iflags, NULL, "handle (do-request-all)");
-
-        if (refresh_all_type == REFRESH_ALL_TYPE_LINKS) {
-            nm_assert(
-                (priv->delayed_action.list_refresh_link->len > 0)
-                == NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK));
-            if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) {
-                _LOGt_delayed_action(DELAYED_ACTION_TYPE_REFRESH_LINK,
-                                     NULL,
-                                     "clear (do-request-all)");
-                priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK;
-                g_ptr_array_set_size(priv->delayed_action.list_refresh_link, 0);
-            }
-        }
-
-        event_handler_read_netlink(platform, FALSE);
-
-        nlmsg = _nl_msg_new_dump(refresh_all_info->obj_type, refresh_all_info->addr_family);
-        if (!nlmsg)
-            goto next_after_fail;
-
-        if (_nl_send_nlmsg(platform,
-                           nlmsg,
-                           NULL,
-                           NULL,
-                           DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS,
-                           out_refresh_all_in_progress)
-            < 0)
-            goto next_after_fail;
-
-        continue;
-
-next_after_fail:
-        nm_assert(*out_refresh_all_in_progress > 0);
-        *out_refresh_all_in_progress -= 1;
-    }
-}
-
-static void
-do_request_one_type_by_needle_object(NMPlatform *platform, const NMPObject *obj_needle)
-{
-    do_request_all_no_delayed_actions(platform,
-                                      delayed_action_refresh_from_needle_object(obj_needle));
-    delayed_action_handle_all(platform, FALSE);
-}
-
-static void
-event_seq_check_refresh_all(NMPlatform *platform, guint32 seq_number)
-{
-    NMLinuxPlatformPrivate *            priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    DelayedActionWaitForNlResponseData *data;
-    guint                               i;
-
-    if (NM_IN_SET(seq_number, 0, priv->nlh_seq_last_seen))
-        return;
-
-    if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
-        nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0);
-
-        for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) {
-            data = &g_array_index(priv->delayed_action.list_wait_for_nl_response,
-                                  DelayedActionWaitForNlResponseData,
-                                  i);
-
-            if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS
-                && data->response.out_refresh_all_in_progress
-                && data->seq_number == priv->nlh_seq_last_seen) {
-                *data->response.out_refresh_all_in_progress -= 1;
-                data->response.out_refresh_all_in_progress = NULL;
-                break;
-            }
-        }
-    }
-
-    priv->nlh_seq_last_seen = seq_number;
-}
-
-static void
-event_seq_check(NMPlatform *            platform,
-                guint32                 seq_number,
-                WaitForNlResponseResult seq_result,
-                const char *            msg)
-{
-    NMLinuxPlatformPrivate *            priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    DelayedActionWaitForNlResponseData *data;
-    guint                               i;
-
-    if (seq_number == 0)
-        return;
-
-    if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
-        nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0);
-
-        for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) {
-            data = &g_array_index(priv->delayed_action.list_wait_for_nl_response,
-                                  DelayedActionWaitForNlResponseData,
-                                  i);
-
-            if (data->seq_number == seq_number) {
-                /* We potentially receive many parts partial responses for the same sequence number.
-                 * Thus, we only remember the result, and collect it later. */
-                if (data->seq_result < 0) {
-                    /* we already saw an error for this sequence number.
-                     * Preserve it. */
-                } else if (seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN
-                           || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN)
-                    data->seq_result = seq_result;
-                if (data->out_errmsg && !*data->out_errmsg)
-                    *data->out_errmsg = g_strdup(msg);
-                return;
-            }
-        }
-    }
-
-#if NM_MORE_LOGGING
-    if (seq_number != priv->nlh_seq_last_handled)
-        _LOGt("netlink: recvmsg: unwaited sequence number %u", seq_number);
-    priv->nlh_seq_last_handled = seq_number;
-#endif
-}
-
-static void
-event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events)
-{
-    NMLinuxPlatformPrivate *priv;
-    nm_auto_nmpobj NMPObject *obj = NULL;
-    NMPCacheOpsType           cache_op;
-    struct nlmsghdr *         msghdr;
-    char                      buf_nlmsghdr[400];
-    gboolean                  is_del  = FALSE;
-    gboolean                  is_dump = FALSE;
-    NMPCache *                cache   = nm_platform_get_cache(platform);
-
-    msghdr = nlmsg_hdr(msg);
-
-    if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)
-        && msghdr->nlmsg_type == RTM_NEWADDR) {
-        /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6,
-         * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */
-        if (nlmsg_valid_hdr(msghdr, sizeof(struct ifaddrmsg))
-            && NM_IN_SET(((struct ifaddrmsg *) nlmsg_data(msghdr))->ifa_family,
-                         AF_INET,
-                         AF_INET6)) {
-            /* see if the nl_msg contains the IFA_FLAGS attribute. If it does,
-             * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR
-             * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14,
-             * dated 30 March, 2014.
-             *
-             * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy
-             * way to detect kernel support. */
-            _nm_platform_kernel_support_init(
-                NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS,
-                !!nlmsg_find_attr(msghdr, sizeof(struct ifaddrmsg), IFA_FLAGS) ? 1 : -1);
-        }
-    }
-
-    if (!handle_events)
-        return;
-
-    if (NM_IN_SET(msghdr->nlmsg_type,
-                  RTM_DELLINK,
-                  RTM_DELADDR,
-                  RTM_DELROUTE,
-                  RTM_DELRULE,
-                  RTM_DELQDISC,
-                  RTM_DELTFILTER)) {
-        /* The event notifies about a deleted object. We don't need to initialize all
-         * fields of the object. */
-        is_del = TRUE;
-    }
-
-    obj = nmp_object_new_from_nl(platform, cache, msg, is_del);
-    if (!obj) {
-        _LOGT("event-notification: %s: ignore",
-              nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)));
-        return;
-    }
-
-    if (!is_del
-        && NM_IN_SET(msghdr->nlmsg_type,
-                     RTM_NEWADDR,
-                     RTM_NEWLINK,
-                     RTM_NEWROUTE,
-                     RTM_NEWRULE,
-                     RTM_NEWQDISC,
-                     RTM_NEWTFILTER)) {
-        is_dump =
-            delayed_action_refresh_all_in_progress(platform,
-                                                   delayed_action_refresh_from_needle_object(obj));
-    }
-
-    _LOGT("event-notification: %s%s: %s",
-          nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)),
-          is_dump ? ", in-dump" : "",
-          nmp_object_to_string(obj,
-                               is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC,
-                               NULL,
-                               0));
-
-    {
-        nm_auto_nmpobj const NMPObject *obj_old = NULL;
-        nm_auto_nmpobj const NMPObject *obj_new = NULL;
-
-        switch (msghdr->nlmsg_type) {
-        case RTM_GETLINK:
-        case RTM_NEWADDR:
-        case RTM_NEWLINK:
-        case RTM_NEWQDISC:
-        case RTM_NEWRULE:
-        case RTM_NEWTFILTER:
-            cache_op = nmp_cache_update_netlink(cache, obj, is_dump, &obj_old, &obj_new);
-            if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
-                cache_on_change(platform, cache_op, obj_old, obj_new);
-                nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new);
-            }
-            break;
-
-        case RTM_NEWROUTE:
-        {
-            nm_auto_nmpobj const NMPObject *obj_replace     = NULL;
-            gboolean                        resync_required = FALSE;
-            gboolean                        only_dirty      = FALSE;
-            gboolean                        is_ipv6;
-
-            /* IPv4 routes that are a response to RTM_GETROUTE must have
-             * the cloned flag while IPv6 routes don't have to. */
-            is_ipv6 = NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ROUTE;
-            if (is_ipv6 || NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, RTM_F_CLONED)) {
-                nm_assert(is_ipv6 || !nmp_object_is_alive(obj));
-                priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-                if (NM_FLAGS_HAS(priv->delayed_action.flags,
-                                 DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
-                    guint i;
-
-                    nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0);
-                    for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) {
-                        DelayedActionWaitForNlResponseData *data =
-                            &g_array_index(priv->delayed_action.list_wait_for_nl_response,
-                                           DelayedActionWaitForNlResponseData,
-                                           i);
-
-                        if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET
-                            && data->response.out_route_get) {
-                            nm_assert(!*data->response.out_route_get);
-                            if (data->seq_number == nlmsg_hdr(msg)->nlmsg_seq) {
-                                *data->response.out_route_get = nmp_object_clone(obj, FALSE);
-                                data->response.out_route_get  = NULL;
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-
-            cache_op = nmp_cache_update_netlink_route(cache,
-                                                      obj,
-                                                      is_dump,
-                                                      msghdr->nlmsg_flags,
-                                                      &obj_old,
-                                                      &obj_new,
-                                                      &obj_replace,
-                                                      &resync_required);
-            if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
-                if (obj_replace) {
-                    const NMDedupMultiEntry *entry_replace;
-
-                    /* we found an object that is to be replaced by the RTM_NEWROUTE message.
-                     * While we invoke the signal, the platform cache might change and invalidate
-                     * the findings. Mitigate that (for the most part), by marking the entry as
-                     * dirty and only delete @obj_replace if it is still dirty afterwards.
-                     *
-                     * Yes, there is a tiny tiny chance for still getting it wrong. But in practice,
-                     * the signal handlers do not cause to call the platform again, so the cache
-                     * is not really changing. -- if they would, it would anyway be dangerous to overflow
-                     * the stack and it's not ensured that the processing of netlink messages is
-                     * reentrant (maybe it is).
-                     */
-                    entry_replace = nmp_cache_lookup_entry(cache, obj_replace);
-                    nm_assert(entry_replace && entry_replace->obj == obj_replace);
-                    nm_dedup_multi_entry_set_dirty(entry_replace, TRUE);
-                    only_dirty = TRUE;
-                }
-                cache_on_change(platform, cache_op, obj_old, obj_new);
-                nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new);
-            }
-
-            if (obj_replace) {
-                /* the RTM_NEWROUTE message indicates that another route was replaced.
-                 * Remove it now. */
-                cache_op = nmp_cache_remove(cache, obj_replace, TRUE, only_dirty, NULL);
-                if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
-                    nm_assert(cache_op == NMP_CACHE_OPS_REMOVED);
-                    cache_on_change(platform, cache_op, obj_replace, NULL);
-                    nm_platform_cache_update_emit_signal(platform, cache_op, obj_replace, NULL);
-                }
-            }
-
-            if (resync_required) {
-                /* we'd like to avoid such resyncs as they are expensive and we should only rely on the
-                 * netlink events. This needs investigation. */
-                _LOGT("schedule resync of routes after RTM_NEWROUTE");
-                delayed_action_schedule(platform,
-                                        delayed_action_refresh_from_needle_object(obj),
-                                        NULL);
-            }
-            break;
-        }
-
-        case RTM_DELADDR:
-        case RTM_DELLINK:
-        case RTM_DELQDISC:
-        case RTM_DELROUTE:
-        case RTM_DELRULE:
-        case RTM_DELTFILTER:
-            cache_op = nmp_cache_remove_netlink(cache, obj, &obj_old, &obj_new);
-            if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
-                cache_on_change(platform, cache_op, obj_old, obj_new);
-                nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new);
-            }
-            break;
-        default:
-            break;
-        }
-    }
-}
-
-/*****************************************************************************/
-
-static int
-do_add_link_with_lookup(NMPlatform *           platform,
-                        NMLinkType             link_type,
-                        const char *           name,
-                        struct nl_msg *        nlmsg,
-                        const NMPlatformLink **out_link)
-{
-    const NMPObject *       obj        = NULL;
-    WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-    gs_free char *          errmsg     = NULL;
-    int                     nle;
-    char                    s_buf[256];
-    NMPCache *              cache = nm_platform_get_cache(platform);
-
-    event_handler_read_netlink(platform, FALSE);
-
-    nle = _nl_send_nlmsg(platform,
-                         nlmsg,
-                         &seq_result,
-                         &errmsg,
-                         DELAYED_ACTION_RESPONSE_TYPE_VOID,
-                         NULL);
-    if (nle < 0) {
-        _LOGE("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)",
-              name,
-              nm_link_type_to_string(link_type),
-              nm_strerror(nle),
-              -nle);
-        NM_SET_OUT(out_link, NULL);
-        return nle;
-    }
-
-    delayed_action_handle_all(platform, FALSE);
-
-    nm_assert(seq_result);
-
-    _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN,
-           "do-add-link[%s/%s]: %s",
-           name,
-           nm_link_type_to_string(link_type),
-           wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)));
-
-    if (out_link) {
-        obj       = nmp_cache_lookup_link_full(cache, 0, name, FALSE, link_type, NULL, NULL);
-        *out_link = NMP_OBJECT_CAST_LINK(obj);
-    }
-
-    return wait_for_nl_response_to_nmerr(seq_result);
-}
-
-static int
-do_add_addrroute(NMPlatform *     platform,
-                 const NMPObject *obj_id,
-                 struct nl_msg *  nlmsg,
-                 gboolean         suppress_netlink_failure)
-{
-    WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-    gs_free char *          errmsg     = NULL;
-    int                     nle;
-    char                    s_buf[256];
-
-    nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id),
-                        NMP_OBJECT_TYPE_IP4_ADDRESS,
-                        NMP_OBJECT_TYPE_IP6_ADDRESS,
-                        NMP_OBJECT_TYPE_IP4_ROUTE,
-                        NMP_OBJECT_TYPE_IP6_ROUTE));
-
-    event_handler_read_netlink(platform, FALSE);
-
-    nle = _nl_send_nlmsg(platform,
-                         nlmsg,
-                         &seq_result,
-                         &errmsg,
-                         DELAYED_ACTION_RESPONSE_TYPE_VOID,
-                         NULL);
-    if (nle < 0) {
-        _LOGE("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)",
-              NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name,
-              nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
-              nm_strerror(nle),
-              -nle);
-        return -NME_PL_NETLINK;
-    }
-
-    delayed_action_handle_all(platform, FALSE);
-
-    nm_assert(seq_result);
-
-    _NMLOG((seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK
-            || (suppress_netlink_failure && seq_result < 0))
-               ? LOGL_DEBUG
-               : LOGL_WARN,
-           "do-add-%s[%s]: %s",
-           NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name,
-           nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
-           wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)));
-
-    if (NMP_OBJECT_GET_TYPE(obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) {
-        /* In rare cases, the object is not yet ready as we received the ACK from
-         * kernel. Need to refetch.
-         *
-         * We want to safe the expensive refetch, thus we look first into the cache
-         * whether the object exists.
-         *
-         * rh#1484434 */
-        if (!nmp_cache_lookup_obj(nm_platform_get_cache(platform), obj_id))
-            do_request_one_type_by_needle_object(platform, obj_id);
-    }
-
-    return wait_for_nl_response_to_nmerr(seq_result);
-}
-
-static gboolean
-do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg)
-{
-    WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-    gs_free char *          errmsg     = NULL;
-    int                     nle;
-    char                    s_buf[256];
-    gboolean                success;
-    const char *            log_detail = "";
-
-    event_handler_read_netlink(platform, FALSE);
-
-    nle = _nl_send_nlmsg(platform,
-                         nlmsg,
-                         &seq_result,
-                         &errmsg,
-                         DELAYED_ACTION_RESPONSE_TYPE_VOID,
-                         NULL);
-    if (nle < 0) {
-        _LOGE("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)",
-              NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name,
-              nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
-              nm_strerror(nle),
-              -nle);
-        return FALSE;
-    }
-
-    delayed_action_handle_all(platform, FALSE);
-
-    nm_assert(seq_result);
-
-    success = TRUE;
-    if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) {
-        /* ok */
-    } else if (NM_IN_SET(-((int) seq_result), ESRCH, ENOENT))
-        log_detail = ", meaning the object was already removed";
-    else if (NM_IN_SET(-((int) seq_result), ENXIO)
-             && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) {
-        /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */
-        log_detail = ", meaning the address was already removed";
-    } else if (NM_IN_SET(-((int) seq_result), EADDRNOTAVAIL)
-               && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id),
-                            NMP_OBJECT_TYPE_IP4_ADDRESS,
-                            NMP_OBJECT_TYPE_IP6_ADDRESS))
-        log_detail = ", meaning the address was already removed";
-    else
-        success = FALSE;
-
-    _NMLOG(success ? LOGL_DEBUG : LOGL_WARN,
-           "do-delete-%s[%s]: %s%s",
-           NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name,
-           nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
-           wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)),
-           log_detail);
-
-    if (NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id),
-                  NMP_OBJECT_TYPE_IP6_ADDRESS,
-                  NMP_OBJECT_TYPE_QDISC,
-                  NMP_OBJECT_TYPE_TFILTER)) {
-        /* In rare cases, the object is still there after we receive the ACK from
-         * kernel. Need to refetch.
-         *
-         * We want to safe the expensive refetch, thus we look first into the cache
-         * whether the object exists.
-         *
-         * rh#1484434 */
-        if (nmp_cache_lookup_obj(nm_platform_get_cache(platform), obj_id))
-            do_request_one_type_by_needle_object(platform, obj_id);
-    }
-
-    return success;
-}
-
-static int
-do_change_link(NMPlatform *          platform,
-               ChangeLinkType        change_link_type,
-               int                   ifindex,
-               struct nl_msg *       nlmsg,
-               const ChangeLinkData *data)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    int                         nle;
-    WaitForNlResponseResult     seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-    gs_free char *              errmsg     = NULL;
-    char                        s_buf[256];
-    int                         result          = 0;
-    NMLogLevel                  log_level       = LOGL_DEBUG;
-    const char *                log_result      = "failure";
-    const char *                log_detail      = "";
-    gs_free char *              log_detail_free = NULL;
-    const NMPObject *           obj_cache;
-
-    if (!nm_platform_netns_push(platform, &netns)) {
-        log_level  = LOGL_ERR;
-        log_detail = ", failure to change network namespace";
-        goto out;
-    }
-
-retry:
-    nle = _nl_send_nlmsg(platform,
-                         nlmsg,
-                         &seq_result,
-                         &errmsg,
-                         DELAYED_ACTION_RESPONSE_TYPE_VOID,
-                         NULL);
-    if (nle < 0) {
-        log_level = LOGL_ERR;
-        log_detail_free =
-            g_strdup_printf(", failure sending netlink request: %s (%d)", nm_strerror(nle), -nle);
-        log_detail = log_detail_free;
-        goto out;
-    }
-
-    /* always refetch the link after changing it. There seems to be issues
-     * and we sometimes lack events. Nuke it from the orbit... */
-    delayed_action_schedule(platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER(ifindex));
-
-    delayed_action_handle_all(platform, FALSE);
-
-    nm_assert(seq_result);
-
-    if (NM_IN_SET(-((int) seq_result), EOPNOTSUPP) && nlmsg_hdr(nlmsg)->nlmsg_type == RTM_NEWLINK) {
-        nlmsg_hdr(nlmsg)->nlmsg_type = RTM_SETLINK;
-        goto retry;
-    }
-
-    if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) {
-        log_result = "success";
-    } else if (NM_IN_SET(-((int) seq_result), EEXIST, EADDRINUSE)) {
-        /* */
-    } else if (NM_IN_SET(-((int) seq_result), ESRCH, ENOENT)) {
-        log_detail = ", firmware not found";
-        result     = -NME_PL_NO_FIRMWARE;
-    } else if (NM_IN_SET(-((int) seq_result), ERANGE)
-               && change_link_type == CHANGE_LINK_TYPE_SET_MTU) {
-        log_detail = ", setting MTU to requested size is not possible";
-        result     = -NME_PL_CANT_SET_MTU;
-    } else if (NM_IN_SET(-((int) seq_result), ENFILE)
-               && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS
-               && (obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex))
-               && obj_cache->link.l_address.len == data->set_address.length
-               && memcmp(obj_cache->link.l_address.data,
-                         data->set_address.address,
-                         data->set_address.length)
-                      == 0) {
-        /* workaround ENFILE which may be wrongly returned (bgo #770456).
-         * If the MAC address is as expected, assume success? */
-        log_result = "success";
-        log_detail = " (assume success changing address)";
-        result     = 0;
-    } else if (NM_IN_SET(-((int) seq_result), ENODEV)) {
-        log_level = LOGL_DEBUG;
-        result    = -NME_PL_NOT_FOUND;
-    } else if (-((int) seq_result) == EAFNOSUPPORT) {
-        log_level = LOGL_DEBUG;
-        result    = -NME_PL_OPNOTSUPP;
-    } else {
-        log_level = LOGL_WARN;
-        result    = -NME_UNSPEC;
-    }
-
-out:
-    _NMLOG(log_level,
-           "do-change-link[%d]: %s changing link: %s%s",
-           ifindex,
-           log_result,
-           wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)),
-           log_detail);
-    return result;
-}
-
-static int
-link_add(NMPlatform *           platform,
-         NMLinkType             type,
-         const char *           name,
-         int                    parent,
-         const void *           address,
-         size_t                 address_len,
-         guint32                mtu,
-         gconstpointer          extra_data,
-         const NMPlatformLink **out_link)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-
-    if (type == NM_LINK_TYPE_BOND) {
-        /* When the kernel loads the bond module, either via explicit modprobe
-         * or automatically in response to creating a bond master, it will also
-         * create a 'bond0' interface.  Since the bond we're about to create may
-         * or may not be named 'bond0' prevent potential confusion about a bond
-         * that the user didn't want by telling the bonding module not to create
-         * bond0 automatically.
-         */
-        if (!g_file_test("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS))
-            (void) nm_utils_modprobe(NULL, TRUE, "bonding", "max_bonds=0", NULL);
-    }
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, name);
-    if (!nlmsg)
-        return -NME_UNSPEC;
-
-    if (parent > 0)
-        NLA_PUT_U32(nlmsg, IFLA_LINK, parent);
-
-    if (address && address_len)
-        NLA_PUT(nlmsg, IFLA_ADDRESS, address_len, address);
-
-    if (mtu)
-        NLA_PUT_U32(nlmsg, IFLA_MTU, mtu);
-
-    if (!_nl_msg_new_link_set_linkinfo(nlmsg, type, extra_data))
-        return -NME_UNSPEC;
-
-    return do_add_link_with_lookup(platform, type, name, nlmsg, out_link);
-nla_put_failure:
-    g_return_val_if_reached(-NME_BUG);
-}
-
-static gboolean
-link_delete(NMPlatform *platform, int ifindex)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    NMPObject                    obj_id;
-    const NMPObject *            obj;
-
-    obj = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex);
-    if (!obj || !obj->_link.netlink.is_in_netlink)
-        return FALSE;
-
-    nlmsg = _nl_msg_new_link(RTM_DELLINK, 0, ifindex, NULL);
-
-    nmp_object_stackinit_id_link(&obj_id, ifindex);
-    return do_delete_object(platform, &obj_id, nlmsg);
-}
-
-static gboolean
-link_refresh(NMPlatform *platform, int ifindex)
-{
-    do_request_link(platform, ifindex, NULL);
-    return !!nm_platform_link_get_obj(platform, ifindex, TRUE);
-}
-
-static gboolean
-link_set_netns(NMPlatform *platform, int ifindex, int netns_fd)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg)
-        return FALSE;
-
-    NLA_PUT(nlmsg, IFLA_NET_NS_FD, 4, &netns_fd);
-    return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static int
-link_change_flags(NMPlatform *platform, int ifindex, unsigned flags_mask, unsigned flags_set)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    char                         s_flags[100];
-
-    _LOGD("link: change %d: flags: set 0x%x/0x%x ([%s] / [%s])",
-          ifindex,
-          flags_set,
-          flags_mask,
-          nm_platform_link_flags2str(flags_set, s_flags, sizeof(s_flags)),
-          nm_platform_link_flags2str(flags_mask, NULL, 0));
-
-    nlmsg = _nl_msg_new_link_full(RTM_NEWLINK, 0, ifindex, NULL, AF_UNSPEC, flags_mask, flags_set);
-    if (!nlmsg)
-        return -NME_UNSPEC;
-    return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL);
-}
-
-static gboolean
-link_set_up(NMPlatform *platform, int ifindex, gboolean *out_no_firmware)
-{
-    int r;
-
-    r = link_change_flags(platform, ifindex, IFF_UP, IFF_UP);
-    NM_SET_OUT(out_no_firmware, (r == -NME_PL_NO_FIRMWARE));
-    return r >= 0;
-}
-
-static gboolean
-link_set_down(NMPlatform *platform, int ifindex)
-{
-    return (link_change_flags(platform, ifindex, IFF_UP, 0) >= 0);
-}
-
-static gboolean
-link_set_arp(NMPlatform *platform, int ifindex)
-{
-    return (link_change_flags(platform, ifindex, IFF_NOARP, 0) >= 0);
-}
-
-static gboolean
-link_set_noarp(NMPlatform *platform, int ifindex)
-{
-    return (link_change_flags(platform, ifindex, IFF_NOARP, IFF_NOARP) >= 0);
-}
-
-static int
-link_set_user_ipv6ll_enabled(NMPlatform *platform, int ifindex, gboolean enabled)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64;
-
-    _LOGD("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s",
-          ifindex,
-          nm_platform_link_inet6_addrgenmode2str(mode, NULL, 0));
-
-    if (!nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) {
-        _LOGD("link: change %d: user-ipv6ll: not supported", ifindex);
-        return -NME_PL_OPNOTSUPP;
-    }
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg || !_nl_msg_new_link_set_afspec(nlmsg, mode, NULL))
-        g_return_val_if_reached(-NME_BUG);
-
-    return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL);
-}
-
-static gboolean
-link_set_token(NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    char                         sbuf[NM_UTILS_INET_ADDRSTRLEN];
-
-    _LOGD("link: change %d: token: set IPv6 address generation token to %s",
-          ifindex,
-          nm_utils_inet6_interface_identifier_to_token(iid, sbuf));
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg || !_nl_msg_new_link_set_afspec(nlmsg, -1, &iid))
-        g_return_val_if_reached(FALSE);
-
-    return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
-}
-
-static gboolean
-link_supports_carrier_detect(NMPlatform *platform, int ifindex)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-
-    if (!nm_platform_netns_push(platform, &netns))
-        return FALSE;
-
-    /* We use netlink for the actual carrier detection, but netlink can't tell
-     * us whether the device actually supports carrier detection in the first
-     * place. We assume any device that does implements one of these two APIs.
-     */
-    return nmp_utils_ethtool_supports_carrier_detect(ifindex)
-           || nmp_utils_mii_supports_carrier_detect(ifindex);
-}
-
-static gboolean
-link_supports_vlans(NMPlatform *platform, int ifindex)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    const NMPObject *           obj;
-
-    obj = nm_platform_link_get_obj(platform, ifindex, TRUE);
-
-    /* Only ARPHRD_ETHER links can possibly support VLANs. */
-    if (!obj || obj->link.arptype != ARPHRD_ETHER)
-        return FALSE;
-
-    if (!nm_platform_netns_push(platform, &netns))
-        return FALSE;
-
-    return nmp_utils_ethtool_supports_vlans(ifindex);
-}
-
-static gboolean
-link_supports_sriov(NMPlatform *platform, int ifindex)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    nm_auto_close int           dirfd = -1;
-    char                        ifname[IFNAMSIZ];
-    int                         num = -1;
-
-    if (!nm_platform_netns_push(platform, &netns))
-        return FALSE;
-
-    dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname);
-    if (dirfd < 0)
-        return FALSE;
-
-    num =
-        nm_platform_sysctl_get_int32(platform,
-                                     NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"),
-                                     -1);
-
-    return num != -1;
-}
-
-static int
-link_set_address(NMPlatform *platform, int ifindex, gconstpointer address, size_t length)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    const ChangeLinkData         d     = {
-        .set_address =
-            {
-                .address = address,
-                .length  = length,
-            },
-    };
-
-    if (!address || !length)
-        g_return_val_if_reached(-NME_BUG);
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg)
-        g_return_val_if_reached(-NME_BUG);
-
-    NLA_PUT(nlmsg, IFLA_ADDRESS, length, address);
-
-    return do_change_link(platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d);
-nla_put_failure:
-    g_return_val_if_reached(-NME_BUG);
-}
-
-static int
-link_set_name(NMPlatform *platform, int ifindex, const char *name)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg)
-        g_return_val_if_reached(-NME_BUG);
-
-    NLA_PUT(nlmsg, IFLA_IFNAME, strlen(name) + 1, name);
-
-    return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static gboolean
-link_get_permanent_address(NMPlatform *platform, int ifindex, guint8 *buf, size_t *length)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-
-    if (!nm_platform_netns_push(platform, &netns))
-        return FALSE;
-
-    return nmp_utils_ethtool_get_permanent_address(ifindex, buf, length);
-}
-
-static int
-link_set_mtu(NMPlatform *platform, int ifindex, guint32 mtu)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg)
-        return FALSE;
-
-    NLA_PUT_U32(nlmsg, IFLA_MTU, mtu);
-
-    return do_change_link(platform, CHANGE_LINK_TYPE_SET_MTU, ifindex, nlmsg, NULL);
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static void
-sriov_idle_cb(gpointer user_data, GCancellable *cancellable)
-{
-    gs_unref_object NMPlatform *platform  = NULL;
-    gs_free_error GError *cancelled_error = NULL;
-    gs_free_error GError *  error         = NULL;
-    NMPlatformAsyncCallback callback;
-    gpointer                callback_data;
-
-    g_cancellable_set_error_if_cancelled(cancellable, &cancelled_error);
-    nm_utils_user_data_unpack(user_data, &platform, &error, &callback, &callback_data);
-    callback(cancelled_error ?: error, callback_data);
-}
-
-static void
-link_set_sriov_params_async(NMPlatform *            platform,
-                            int                     ifindex,
-                            guint                   num_vfs,
-                            NMOptionBool            autoprobe,
-                            NMPlatformAsyncCallback callback,
-                            gpointer                data,
-                            GCancellable *          cancellable)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    gs_free_error GError *error       = NULL;
-    nm_auto_close int     dirfd       = -1;
-    int                   current_autoprobe;
-    guint                 i, total;
-    gint64                current_num;
-    char                  ifname[IFNAMSIZ];
-    gpointer              packed;
-    const char *          values[3];
-    char                  buf[64];
-
-    g_return_if_fail(callback || !data);
-    g_return_if_fail(cancellable);
-
-    if (!nm_platform_netns_push(platform, &netns)) {
-        g_set_error_literal(&error,
-                            NM_UTILS_ERROR,
-                            NM_UTILS_ERROR_UNKNOWN,
-                            "couldn't change namespace");
-        goto out_idle;
-    }
-
-    dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname);
-    if (!dirfd) {
-        g_set_error_literal(&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "couldn't open netdir");
-        goto out_idle;
-    }
-
-    total = nm_platform_sysctl_get_int_checked(
-        platform,
-        NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_totalvfs"),
-        10,
-        0,
-        G_MAXUINT,
-        0);
-    if (!errno && num_vfs > total) {
-        _LOGW("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs);
-        num_vfs = total;
-    }
-
-    /*
-     * Take special care when setting new values:
-     *  - don't touch anything if the right values are already set
-     *  - to change the number of VFs or autoprobe we need to destroy existing VFs
-     *  - the autoprobe setting is irrelevant when numvfs is zero
-     */
-    current_num = nm_platform_sysctl_get_int_checked(
-        platform,
-        NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"),
-        10,
-        0,
-        G_MAXUINT,
-        -1);
-    current_autoprobe = nm_platform_sysctl_get_int_checked(
-        platform,
-        NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_drivers_autoprobe"),
-        10,
-        0,
-        1,
-        -1);
-
-    if (current_autoprobe == -1 && errno == ENOENT) {
-        /* older kernel versions don't have this sysctl. Assume the value is
-         * "1". */
-        current_autoprobe = 1;
-    }
-
-    if (current_num == num_vfs
-        && (autoprobe == NM_OPTION_BOOL_DEFAULT || current_autoprobe == autoprobe))
-        goto out_idle;
-
-    if (NM_IN_SET(autoprobe, NM_OPTION_BOOL_TRUE, NM_OPTION_BOOL_FALSE)
-        && current_autoprobe != autoprobe
-        && !nm_platform_sysctl_set(
-            NM_PLATFORM_GET,
-            NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_drivers_autoprobe"),
-            nm_sprintf_buf(buf, "%d", (int) autoprobe))) {
-        g_set_error(&error,
-                    NM_UTILS_ERROR,
-                    NM_UTILS_ERROR_UNKNOWN,
-                    "couldn't set SR-IOV drivers-autoprobe to %d: %s",
-                    (int) autoprobe,
-                    nm_strerror_native(errno));
-        goto out_idle;
-    }
-
-    if (current_num == 0 && num_vfs == 0)
-        goto out_idle;
-
-    i = 0;
-    if (current_num != 0)
-        values[i++] = "0";
-    if (num_vfs != 0)
-        values[i++] = nm_sprintf_bufa(32, "%u", num_vfs);
-    values[i++] = NULL;
-
-    sysctl_set_async(platform,
-                     NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"),
-                     values,
-                     callback,
-                     data,
-                     cancellable);
-    return;
-
-out_idle:
-    if (callback) {
-        packed = nm_utils_user_data_pack(g_object_ref(platform),
-                                         g_steal_pointer(&error),
-                                         callback,
-                                         data);
-        nm_utils_invoke_on_idle(cancellable, sriov_idle_cb, packed);
-    }
-}
-
-static gboolean
-link_set_sriov_vfs(NMPlatform *platform, int ifindex, const NMPlatformVF *const *vfs)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    struct nlattr *              list, *info, *vlan_list;
-    guint                        i;
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg)
-        g_return_val_if_reached(-NME_BUG);
-
-    if (!(list = nla_nest_start(nlmsg, IFLA_VFINFO_LIST)))
-        goto nla_put_failure;
-
-    for (i = 0; vfs[i]; i++) {
-        const NMPlatformVF *vf = vfs[i];
-
-        if (!(info = nla_nest_start(nlmsg, IFLA_VF_INFO)))
-            goto nla_put_failure;
-
-        if (vf->spoofchk >= 0) {
-            struct _ifla_vf_setting ivs = {0};
-
-            ivs.vf      = vf->index;
-            ivs.setting = vf->spoofchk;
-            NLA_PUT(nlmsg, IFLA_VF_SPOOFCHK, sizeof(ivs), &ivs);
-        }
-
-        if (vf->trust >= 0) {
-            struct _ifla_vf_setting ivs = {0};
-
-            ivs.vf      = vf->index;
-            ivs.setting = vf->trust;
-            NLA_PUT(nlmsg, IFLA_VF_TRUST, sizeof(ivs), &ivs);
-        }
-
-        if (vf->mac.len) {
-            struct ifla_vf_mac ivm = {0};
-
-            ivm.vf = vf->index;
-            memcpy(ivm.mac, vf->mac.data, vf->mac.len);
-            NLA_PUT(nlmsg, IFLA_VF_MAC, sizeof(ivm), &ivm);
-        }
-
-        if (vf->min_tx_rate || vf->max_tx_rate) {
-            struct _ifla_vf_rate ivr = {0};
-
-            ivr.vf          = vf->index;
-            ivr.min_tx_rate = vf->min_tx_rate;
-            ivr.max_tx_rate = vf->max_tx_rate;
-            NLA_PUT(nlmsg, IFLA_VF_RATE, sizeof(ivr), &ivr);
-        }
-
-        /* Kernel only supports one VLAN per VF now. If this
-         * changes in the future, we need to figure out how to
-         * clear existing VLANs and set new ones in one message
-         * with the new API.*/
-        if (vf->num_vlans > 1) {
-            _LOGW("multiple VLANs per VF are not supported at the moment");
-            return FALSE;
-        } else {
-            struct _ifla_vf_vlan_info ivvi = {0};
-
-            if (!(vlan_list = nla_nest_start(nlmsg, IFLA_VF_VLAN_LIST)))
-                goto nla_put_failure;
-
-            ivvi.vf = vf->index;
-            if (vf->num_vlans == 1) {
-                ivvi.vlan       = vf->vlans[0].id;
-                ivvi.qos        = vf->vlans[0].qos;
-                ivvi.vlan_proto = htons(vf->vlans[0].proto_ad ? ETH_P_8021AD : ETH_P_8021Q);
-            } else {
-                /* Clear existing VLAN */
-                ivvi.vlan       = 0;
-                ivvi.qos        = 0;
-                ivvi.vlan_proto = htons(ETH_P_8021Q);
-            }
-
-            NLA_PUT(nlmsg, IFLA_VF_VLAN_INFO, sizeof(ivvi), &ivvi);
-            nla_nest_end(nlmsg, vlan_list);
-        }
-        nla_nest_end(nlmsg, info);
-    }
-    nla_nest_end(nlmsg, list);
-
-    return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static gboolean
-link_set_bridge_vlans(NMPlatform *                       platform,
-                      int                                ifindex,
-                      gboolean                           on_master,
-                      const NMPlatformBridgeVlan *const *vlans)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    struct nlattr *              list;
-    struct bridge_vlan_info      vinfo = {};
-    guint                        i;
-
-    nlmsg =
-        _nl_msg_new_link_full(vlans ? RTM_SETLINK : RTM_DELLINK, 0, ifindex, NULL, AF_BRIDGE, 0, 0);
-    if (!nlmsg)
-        g_return_val_if_reached(-NME_BUG);
-
-    if (!(list = nla_nest_start(nlmsg, IFLA_AF_SPEC)))
-        goto nla_put_failure;
-
-    NLA_PUT_U16(nlmsg, IFLA_BRIDGE_FLAGS, on_master ? BRIDGE_FLAGS_MASTER : BRIDGE_FLAGS_SELF);
-
-    if (vlans) {
-        /* Add VLANs */
-        for (i = 0; vlans[i]; i++) {
-            const NMPlatformBridgeVlan *vlan     = vlans[i];
-            gboolean                    is_range = vlan->vid_start != vlan->vid_end;
-
-            vinfo.vid   = vlan->vid_start;
-            vinfo.flags = is_range ? BRIDGE_VLAN_INFO_RANGE_BEGIN : 0;
-
-            if (vlan->untagged)
-                vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
-            if (vlan->pvid)
-                vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
-
-            NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
-
-            if (is_range) {
-                vinfo.vid   = vlan->vid_end;
-                vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END;
-                NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
-            }
-        }
-    } else {
-        /* Flush existing VLANs */
-        vinfo.vid   = 1;
-        vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
-        NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
-
-        vinfo.vid   = 4094;
-        vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END;
-        NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
-    }
-
-    nla_nest_end(nlmsg, list);
-
-    return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static char *
-link_get_physical_port_id(NMPlatform *platform, int ifindex)
-{
-    nm_auto_close int dirfd = -1;
-    char              ifname_verified[IFNAMSIZ];
-
-    dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname_verified);
-    if (dirfd < 0)
-        return NULL;
-    return sysctl_get(platform, NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "phys_port_id"));
-}
-
-static guint
-link_get_dev_id(NMPlatform *platform, int ifindex)
-{
-    nm_auto_close int dirfd = -1;
-    char              ifname_verified[IFNAMSIZ];
-
-    dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname_verified);
-    if (dirfd < 0)
-        return 0;
-    return nm_platform_sysctl_get_int_checked(
-        platform,
-        NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "dev_id"),
-        16,
-        0,
-        G_MAXUINT16,
-        0);
-}
-
-static gboolean
-link_tun_add(NMPlatform *            platform,
-             const char *            name,
-             const NMPlatformLnkTun *props,
-             const NMPlatformLink ** out_link,
-             int *                   out_fd)
-{
-    const NMPObject * obj;
-    struct ifreq      ifr = {};
-    nm_auto_close int fd  = -1;
-
-    nm_assert(NM_IN_SET(props->type, IFF_TAP, IFF_TUN));
-    nm_assert(props->persist || out_fd);
-
-    fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
-    if (fd < 0)
-        return FALSE;
-
-    nm_utils_ifname_cpy(ifr.ifr_name, name);
-    ifr.ifr_flags = ((short) props->type) | ((short) IFF_TUN_EXCL)
-                    | (!props->pi ? (short) IFF_NO_PI : (short) 0)
-                    | (props->vnet_hdr ? (short) IFF_VNET_HDR : (short) 0)
-                    | (props->multi_queue ? (short) NM_IFF_MULTI_QUEUE : (short) 0);
-    if (ioctl(fd, TUNSETIFF, &ifr))
-        return FALSE;
-
-    if (props->owner_valid) {
-        if (ioctl(fd, TUNSETOWNER, (uid_t) props->owner))
-            return FALSE;
-    }
-
-    if (props->group_valid) {
-        if (ioctl(fd, TUNSETGROUP, (gid_t) props->group))
-            return FALSE;
-    }
-
-    if (props->persist) {
-        if (ioctl(fd, TUNSETPERSIST, 1))
-            return FALSE;
-    }
-
-    do_request_link(platform, 0, name);
-    obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform),
-                                     0,
-                                     name,
-                                     FALSE,
-                                     NM_LINK_TYPE_TUN,
-                                     NULL,
-                                     NULL);
-
-    if (!obj)
-        return FALSE;
-
-    NM_SET_OUT(out_link, &obj->link);
-    NM_SET_OUT(out_fd, nm_steal_fd(&fd));
-    return TRUE;
-}
-
-static void
-_vlan_change_vlan_qos_mapping_create(gboolean                is_ingress_map,
-                                     gboolean                reset_all,
-                                     const NMVlanQosMapping *current_map,
-                                     guint                   current_n_map,
-                                     const NMVlanQosMapping *set_map,
-                                     guint                   set_n_map,
-                                     NMVlanQosMapping **     out_map,
-                                     guint *                 out_n_map)
-{
-    NMVlanQosMapping *map;
-    guint             i, j, len;
-    const guint       INGRESS_RANGE_LEN = 8;
-
-    nm_assert(out_map && !*out_map);
-    nm_assert(out_n_map && !*out_n_map);
-
-    if (!reset_all)
-        current_n_map = 0;
-    else if (is_ingress_map)
-        current_n_map = INGRESS_RANGE_LEN;
-
-    len = current_n_map + set_n_map;
-
-    if (len == 0)
-        return;
-
-    map = g_new(NMVlanQosMapping, len);
-
-    if (current_n_map) {
-        if (is_ingress_map) {
-            /* For the ingress-map, there are only 8 entries (0 to 7).
-             * When the user requests to reset all entries, we don't actually
-             * need the cached entries, we can just explicitly clear all possible
-             * ones.
-             *
-             * That makes only a real difference in case our cache is out-of-date.
-             *
-             * For the egress map we cannot do that, because there are far too
-             * many. There we can only clear the entries that we know about. */
-            for (i = 0; i < INGRESS_RANGE_LEN; i++) {
-                map[i].from = i;
-                map[i].to   = 0;
-            }
-        } else {
-            for (i = 0; i < current_n_map; i++) {
-                map[i].from = current_map[i].from;
-                map[i].to   = 0;
-            }
-        }
-    }
-    if (set_n_map)
-        memcpy(&map[current_n_map], set_map, sizeof(*set_map) * set_n_map);
-
-    g_qsort_with_data(map, len, sizeof(*map), _vlan_qos_mapping_cmp_from, NULL);
-
-    for (i = 0, j = 0; i < len; i++) {
-        if ((is_ingress_map && !VLAN_XGRESS_PRIO_VALID(map[i].from))
-            || (!is_ingress_map && !VLAN_XGRESS_PRIO_VALID(map[i].to)))
-            continue;
-        if (j > 0 && map[j - 1].from == map[i].from)
-            map[j - 1] = map[i];
-        else
-            map[j++] = map[i];
-    }
-
-    *out_map   = map;
-    *out_n_map = j;
-}
-
-static gboolean
-link_vlan_change(NMPlatform *            platform,
-                 int                     ifindex,
-                 NMVlanFlags             flags_mask,
-                 NMVlanFlags             flags_set,
-                 gboolean                ingress_reset_all,
-                 const NMVlanQosMapping *ingress_map,
-                 gsize                   n_ingress_map,
-                 gboolean                egress_reset_all,
-                 const NMVlanQosMapping *egress_map,
-                 gsize                   n_egress_map)
-{
-    const NMPObject *            obj_cache;
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    const NMPObjectLnkVlan *     lnk;
-    guint                        new_n_ingress_map = 0;
-    guint                        new_n_egress_map  = 0;
-    gs_free NMVlanQosMapping *new_ingress_map      = NULL;
-    gs_free NMVlanQosMapping *new_egress_map       = NULL;
-
-    obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex);
-    if (!obj_cache || !obj_cache->_link.netlink.is_in_netlink) {
-        _LOGD("link: change %d: %s: link does not exist", ifindex, "vlan");
-        return FALSE;
-    }
-
-    lnk = obj_cache->_link.netlink.lnk ? &obj_cache->_link.netlink.lnk->_lnk_vlan : NULL;
-
-    flags_set &= flags_mask;
-
-    _vlan_change_vlan_qos_mapping_create(TRUE,
-                                         ingress_reset_all,
-                                         lnk ? lnk->ingress_qos_map : NULL,
-                                         lnk ? lnk->n_ingress_qos_map : 0,
-                                         ingress_map,
-                                         n_ingress_map,
-                                         &new_ingress_map,
-                                         &new_n_ingress_map);
-
-    _vlan_change_vlan_qos_mapping_create(FALSE,
-                                         egress_reset_all,
-                                         lnk ? lnk->egress_qos_map : NULL,
-                                         lnk ? lnk->n_egress_qos_map : 0,
-                                         egress_map,
-                                         n_egress_map,
-                                         &new_egress_map,
-                                         &new_n_egress_map);
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg
-        || !_nl_msg_new_link_set_linkinfo_vlan(nlmsg,
-                                               -1,
-                                               flags_mask,
-                                               flags_set,
-                                               new_ingress_map,
-                                               new_n_ingress_map,
-                                               new_egress_map,
-                                               new_n_egress_map))
-        g_return_val_if_reached(FALSE);
-
-    return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
-}
-
-static gboolean
-link_enslave(NMPlatform *platform, int master, int slave)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg   = NULL;
-    int                          ifindex = slave;
-
-    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
-    if (!nlmsg)
-        return FALSE;
-
-    NLA_PUT_U32(nlmsg, IFLA_MASTER, master);
-
-    return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static gboolean
-link_release(NMPlatform *platform, int master, int slave)
-{
-    return link_enslave(platform, 0, slave);
-}
-
-/*****************************************************************************/
-
-static gboolean
-_infiniband_partition_action(NMPlatform *           platform,
-                             InfinibandAction       action,
-                             int                    parent,
-                             int                    p_key,
-                             const NMPlatformLink **out_link)
-{
-    nm_auto_close int dirfd = -1;
-    char              ifname_parent[IFNAMSIZ];
-    const NMPObject * obj;
-    char              id[20];
-    char              name[IFNAMSIZ];
-    gboolean          success;
-
-    nm_assert(NM_IN_SET(action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD));
-    nm_assert(p_key > 0 && p_key <= 0xffff && p_key != 0x8000);
-
-    dirfd = nm_platform_sysctl_open_netdir(platform, parent, ifname_parent);
-    if (dirfd < 0) {
-        errno = ENOENT;
-        return FALSE;
-    }
-
-    nm_sprintf_buf(id, "0x%04x", p_key);
-    if (action == INFINIBAND_ACTION_CREATE_CHILD)
-        success =
-            nm_platform_sysctl_set(platform,
-                                   NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_parent, "create_child"),
-                                   id);
-    else
-        success =
-            nm_platform_sysctl_set(platform,
-                                   NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_parent, "delete_child"),
-                                   id);
-
-    if (!success) {
-        if (action == INFINIBAND_ACTION_DELETE_CHILD && errno == ENODEV)
-            return TRUE;
-        return FALSE;
-    }
-
-    nm_utils_new_infiniband_name(name, ifname_parent, p_key);
-    do_request_link(platform, 0, name);
-
-    if (action == INFINIBAND_ACTION_DELETE_CHILD)
-        return TRUE;
-
-    obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform),
-                                     0,
-                                     name,
-                                     FALSE,
-                                     NM_LINK_TYPE_INFINIBAND,
-                                     NULL,
-                                     NULL);
-    if (out_link)
-        *out_link = obj ? &obj->link : NULL;
-    return !!obj;
-}
-
-static gboolean
-infiniband_partition_add(NMPlatform *           platform,
-                         int                    parent,
-                         int                    p_key,
-                         const NMPlatformLink **out_link)
-{
-    return _infiniband_partition_action(platform,
-                                        INFINIBAND_ACTION_CREATE_CHILD,
-                                        parent,
-                                        p_key,
-                                        out_link);
-}
-
-static gboolean
-infiniband_partition_delete(NMPlatform *platform, int parent, int p_key)
-{
-    return _infiniband_partition_action(platform,
-                                        INFINIBAND_ACTION_DELETE_CHILD,
-                                        parent,
-                                        p_key,
-                                        NULL);
-}
-
-/*****************************************************************************/
-
-static GObject *
-get_ext_data(NMPlatform *platform, int ifindex)
-{
-    const NMPObject *obj;
-
-    obj = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex);
-    if (!obj)
-        return NULL;
-
-    return obj->_link.ext_data;
-}
-
-/*****************************************************************************/
-
-#define WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, retval) \
-    nm_auto_pop_netns NMPNetns *netns = NULL;                          \
-    NMWifiUtils *               wifi_data;                             \
-    if (!nm_platform_netns_push(platform, &netns))                     \
-        return retval;                                                 \
-    wifi_data = NM_WIFI_UTILS(get_ext_data(platform, ifindex));        \
-    if (!wifi_data)                                                    \
-        return retval;
-
-static gboolean
-wifi_get_capabilities(NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE);
-    if (caps)
-        *caps = nm_wifi_utils_get_caps(wifi_data);
-    return TRUE;
-}
-
-static guint32
-wifi_get_frequency(NMPlatform *platform, int ifindex)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0);
-    return nm_wifi_utils_get_freq(wifi_data);
-}
-
-static gboolean
-wifi_get_station(NMPlatform * platform,
-                 int          ifindex,
-                 NMEtherAddr *out_bssid,
-                 int *        out_quality,
-                 guint32 *    out_rate)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE);
-    return nm_wifi_utils_get_station(wifi_data, out_bssid, out_quality, out_rate);
-}
-
-static NM80211Mode
-wifi_get_mode(NMPlatform *platform, int ifindex)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, NM_802_11_MODE_UNKNOWN);
-    return nm_wifi_utils_get_mode(wifi_data);
-}
-
-static void
-wifi_set_mode(NMPlatform *platform, int ifindex, NM80211Mode mode)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, );
-    nm_wifi_utils_set_mode(wifi_data, mode);
-}
-
-static void
-wifi_set_powersave(NMPlatform *platform, int ifindex, guint32 powersave)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, );
-    nm_wifi_utils_set_powersave(wifi_data, powersave);
-}
-
-static guint32
-wifi_find_frequency(NMPlatform *platform, int ifindex, const guint32 *freqs)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0);
-    return nm_wifi_utils_find_freq(wifi_data, freqs);
-}
-
-static void
-wifi_indicate_addressing_running(NMPlatform *platform, int ifindex, gboolean running)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, );
-    nm_wifi_utils_indicate_addressing_running(wifi_data, running);
-}
-
-static NMSettingWirelessWakeOnWLan
-wifi_get_wake_on_wlan(NMPlatform *platform, int ifindex)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE);
-    return nm_wifi_utils_get_wake_on_wlan(wifi_data);
-}
-
-static gboolean
-wifi_set_wake_on_wlan(NMPlatform *platform, int ifindex, NMSettingWirelessWakeOnWLan wowl)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE);
-    return nm_wifi_utils_set_wake_on_wlan(wifi_data, wowl);
-}
-
-/*****************************************************************************/
-
-static gboolean
-link_can_assume(NMPlatform *platform, int ifindex)
-{
-    NMPLookup        lookup;
-    const NMPObject *link, *o;
-    NMDedupMultiIter iter;
-    NMPCache *       cache = nm_platform_get_cache(platform);
-
-    if (ifindex <= 0)
-        return FALSE;
-
-    link = nm_platform_link_get_obj(platform, ifindex, TRUE);
-    if (!link)
-        return FALSE;
-
-    if (!NM_FLAGS_HAS(link->link.n_ifi_flags, IFF_UP))
-        return FALSE;
-
-    if (link->link.master > 0)
-        return TRUE;
-
-    nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex);
-    if (nmp_cache_lookup(cache, &lookup))
-        return TRUE;
-
-    nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex);
-    nmp_cache_iter_for_each (&iter, nmp_cache_lookup(cache, &lookup), &o) {
-        nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP6_ADDRESS);
-        if (!IN6_IS_ADDR_LINKLOCAL(&o->ip6_address.address))
-            return TRUE;
-    }
-    return FALSE;
-}
-
-/*****************************************************************************/
-
-static guint32
-mesh_get_channel(NMPlatform *platform, int ifindex)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0);
-    return nm_wifi_utils_get_mesh_channel(wifi_data);
-}
-
-static gboolean
-mesh_set_channel(NMPlatform *platform, int ifindex, guint32 channel)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE);
-    return nm_wifi_utils_set_mesh_channel(wifi_data, channel);
-}
-
-static gboolean
-mesh_set_ssid(NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len)
-{
-    WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE);
-    return nm_wifi_utils_set_mesh_ssid(wifi_data, ssid, len);
-}
-
-/*****************************************************************************/
-
-#define WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, retval)             \
-    NMWpanUtils *wpan_data = NM_WPAN_UTILS(get_ext_data(platform, ifindex)); \
-    if (!wpan_data)                                                          \
-        return retval;
-
-static guint16
-wpan_get_pan_id(NMPlatform *platform, int ifindex)
-{
-    WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, G_MAXINT16);
-    return nm_wpan_utils_get_pan_id(wpan_data);
-}
-
-static gboolean
-wpan_set_pan_id(NMPlatform *platform, int ifindex, guint16 pan_id)
-{
-    WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE);
-    return nm_wpan_utils_set_pan_id(wpan_data, pan_id);
-}
-
-static guint16
-wpan_get_short_addr(NMPlatform *platform, int ifindex)
-{
-    WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, G_MAXINT16);
-    return nm_wpan_utils_get_short_addr(wpan_data);
-}
-
-static gboolean
-wpan_set_short_addr(NMPlatform *platform, int ifindex, guint16 short_addr)
-{
-    WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE);
-    return nm_wpan_utils_set_short_addr(wpan_data, short_addr);
-}
-
-static gboolean
-wpan_set_channel(NMPlatform *platform, int ifindex, guint8 page, guint8 channel)
-{
-    WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE);
-    return nm_wpan_utils_set_channel(wpan_data, page, channel);
-}
-
-/*****************************************************************************/
-
-static gboolean
-link_get_wake_on_lan(NMPlatform *platform, int ifindex)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    NMLinkType                  type  = nm_platform_link_get_type(platform, ifindex);
-
-    if (!nm_platform_netns_push(platform, &netns))
-        return FALSE;
-
-    if (type == NM_LINK_TYPE_ETHERNET)
-        return nmp_utils_ethtool_get_wake_on_lan(ifindex);
-    else if (type == NM_LINK_TYPE_WIFI) {
-        NMWifiUtils *wifi_data = NM_WIFI_UTILS(get_ext_data(platform, ifindex));
-
-        if (!wifi_data)
-            return FALSE;
-
-        return !NM_IN_SET(nm_wifi_utils_get_wake_on_wlan(wifi_data),
-                          NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE,
-                          NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE);
-
-    } else
-        return FALSE;
-}
-
-static gboolean
-link_get_driver_info(NMPlatform *platform,
-                     int         ifindex,
-                     char **     out_driver_name,
-                     char **     out_driver_version,
-                     char **     out_fw_version)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    NMPUtilsEthtoolDriverInfo   driver_info;
-
-    if (!nm_platform_netns_push(platform, &netns))
-        return FALSE;
-
-    if (!nmp_utils_ethtool_get_driver_info(ifindex, &driver_info))
-        return FALSE;
-    NM_SET_OUT(out_driver_name, g_strdup(driver_info.driver));
-    NM_SET_OUT(out_driver_version, g_strdup(driver_info.version));
-    NM_SET_OUT(out_fw_version, g_strdup(driver_info.fw_version));
-    return TRUE;
-}
-
-/*****************************************************************************/
-
-static gboolean
-ip4_address_add(NMPlatform *platform,
-                int         ifindex,
-                in_addr_t   addr,
-                guint8      plen,
-                in_addr_t   peer_addr,
-                in_addr_t   broadcast_address,
-                guint32     lifetime,
-                guint32     preferred,
-                guint32     flags,
-                const char *label)
-{
-    NMPObject                    obj_id;
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-
-    nlmsg = _nl_msg_new_address(RTM_NEWADDR,
-                                NLM_F_CREATE | NLM_F_REPLACE,
-                                AF_INET,
-                                ifindex,
-                                &addr,
-                                plen,
-                                &peer_addr,
-                                flags,
-                                nm_utils_ip4_address_is_link_local(addr) ? RT_SCOPE_LINK
-                                                                         : RT_SCOPE_UNIVERSE,
-                                lifetime,
-                                preferred,
-                                broadcast_address,
-                                label);
-
-    nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, addr, plen, peer_addr);
-    return (do_add_addrroute(platform, &obj_id, nlmsg, FALSE) >= 0);
-}
-
-static gboolean
-ip6_address_add(NMPlatform *    platform,
-                int             ifindex,
-                struct in6_addr addr,
-                guint8          plen,
-                struct in6_addr peer_addr,
-                guint32         lifetime,
-                guint32         preferred,
-                guint32         flags)
-{
-    NMPObject                    obj_id;
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-
-    nlmsg = _nl_msg_new_address(RTM_NEWADDR,
-                                NLM_F_CREATE | NLM_F_REPLACE,
-                                AF_INET6,
-                                ifindex,
-                                &addr,
-                                plen,
-                                IN6_IS_ADDR_UNSPECIFIED(&peer_addr) ? NULL : &peer_addr,
-                                flags,
-                                RT_SCOPE_UNIVERSE,
-                                lifetime,
-                                preferred,
-                                0,
-                                NULL);
-
-    nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr);
-    return (do_add_addrroute(platform, &obj_id, nlmsg, FALSE) >= 0);
-}
-
-static gboolean
-ip4_address_delete(NMPlatform *platform,
-                   int         ifindex,
-                   in_addr_t   addr,
-                   guint8      plen,
-                   in_addr_t   peer_address)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    NMPObject                    obj_id;
-
-    nlmsg = _nl_msg_new_address(RTM_DELADDR,
-                                0,
-                                AF_INET,
-                                ifindex,
-                                &addr,
-                                plen,
-                                &peer_address,
-                                0,
-                                RT_SCOPE_NOWHERE,
-                                NM_PLATFORM_LIFETIME_PERMANENT,
-                                NM_PLATFORM_LIFETIME_PERMANENT,
-                                0,
-                                NULL);
-    if (!nlmsg)
-        g_return_val_if_reached(FALSE);
-
-    nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, addr, plen, peer_address);
-    return do_delete_object(platform, &obj_id, nlmsg);
-}
-
-static gboolean
-ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    NMPObject                    obj_id;
-
-    nlmsg = _nl_msg_new_address(RTM_DELADDR,
-                                0,
-                                AF_INET6,
-                                ifindex,
-                                &addr,
-                                plen,
-                                NULL,
-                                0,
-                                RT_SCOPE_NOWHERE,
-                                NM_PLATFORM_LIFETIME_PERMANENT,
-                                NM_PLATFORM_LIFETIME_PERMANENT,
-                                0,
-                                NULL);
-    if (!nlmsg)
-        g_return_val_if_reached(FALSE);
-
-    nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr);
-    return do_delete_object(platform, &obj_id, nlmsg);
-}
-
-/*****************************************************************************/
-
-static int
-ip_route_add(NMPlatform *             platform,
-             NMPNlmFlags              flags,
-             int                      addr_family,
-             const NMPlatformIPRoute *route)
-{
-    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-    NMPObject                    obj;
-
-    nmp_object_stackinit(&obj,
-                         NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)),
-                         (const NMPlatformObject *) route);
-
-    nm_platform_ip_route_normalize(addr_family, NMP_OBJECT_CAST_IP_ROUTE(&obj));
-
-    nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj);
-    if (!nlmsg)
-        g_return_val_if_reached(-NME_BUG);
-    return do_add_addrroute(platform,
-                            &obj,
-                            nlmsg,
-                            NM_FLAGS_HAS(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE));
-}
-
-static gboolean
-object_delete(NMPlatform *platform, const NMPObject *obj)
-{
-    nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL;
-    nm_auto_nlmsg struct nl_msg *   nlmsg          = NULL;
-
-    if (!NMP_OBJECT_IS_STACKINIT(obj))
-        obj_keep_alive = nmp_object_ref(obj);
-
-    switch (NMP_OBJECT_GET_TYPE(obj)) {
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-        nlmsg = _nl_msg_new_route(RTM_DELROUTE, 0, obj);
-        break;
-    case NMP_OBJECT_TYPE_ROUTING_RULE:
-        nlmsg = _nl_msg_new_routing_rule(RTM_DELRULE, 0, NMP_OBJECT_CAST_ROUTING_RULE(obj));
-        break;
-    case NMP_OBJECT_TYPE_QDISC:
-        nlmsg = _nl_msg_new_qdisc(RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC(obj));
-        break;
-    case NMP_OBJECT_TYPE_TFILTER:
-        nlmsg = _nl_msg_new_tfilter(RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER(obj));
-        break;
-    default:
-        break;
-    }
-
-    if (!nlmsg)
-        g_return_val_if_reached(FALSE);
-    return do_delete_object(platform, obj, nlmsg);
-}
-
-/*****************************************************************************/
-
-static int
-ip_route_get(NMPlatform *  platform,
-             int           addr_family,
-             gconstpointer address,
-             int           oif_ifindex,
-             NMPObject **  out_route)
-{
-    const gboolean          is_v4     = (addr_family == AF_INET);
-    const int               addr_len  = is_v4 ? 4 : 16;
-    int                     try_count = 0;
-    WaitForNlResponseResult seq_result;
-    int                     nle;
-    nm_auto_nmpobj NMPObject *route = NULL;
-
-    nm_assert(NM_IS_LINUX_PLATFORM(platform));
-    nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
-    nm_assert(address);
-
-    do {
-        struct {
-            struct nlmsghdr n;
-            struct rtmsg    r;
-            char            buf[64];
-        } req = {
-            .n.nlmsg_len   = NLMSG_LENGTH(sizeof(struct rtmsg)),
-            .n.nlmsg_flags = NLM_F_REQUEST,
-            .n.nlmsg_type  = RTM_GETROUTE,
-            .r.rtm_family  = addr_family,
-            .r.rtm_tos     = 0,
-            .r.rtm_dst_len = is_v4 ? 32 : 128,
-            .r.rtm_flags   = 0x1000 /* RTM_F_LOOKUP_TABLE */,
-        };
-
-        nm_clear_pointer(&route, nmp_object_unref);
-
-        if (!_nl_addattr_l(&req.n, sizeof(req), RTA_DST, address, addr_len))
-            nm_assert_not_reached();
-
-        if (oif_ifindex > 0) {
-            gint32 ii = oif_ifindex;
-
-            if (!_nl_addattr_l(&req.n, sizeof(req), RTA_OIF, &ii, sizeof(ii)))
-                nm_assert_not_reached();
-        }
-
-        seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-        nle        = _nl_send_nlmsghdr(platform,
-                                &req.n,
-                                &seq_result,
-                                NULL,
-                                DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET,
-                                &route);
-        if (nle < 0) {
-            _LOGE("get-route: failure sending netlink request \"%s\" (%d)",
-                  nm_strerror_native(-nle),
-                  -nle);
-            return -NME_UNSPEC;
-        }
-
-        delayed_action_handle_all(platform, FALSE);
-
-        /* Retry, if we failed due to a cache resync. That can happen when the netlink
-         * socket fills up and we lost the response. */
-    } while (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC && ++try_count < 10);
-
-    if (seq_result < 0) {
-        /* negative seq_result is an errno from kernel. Map it to negative
-         * int (which are also errno). */
-        return (int) seq_result;
-    }
-
-    if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) {
-        if (route) {
-            NM_SET_OUT(out_route, g_steal_pointer(&route));
-            return 0;
-        }
-        seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN;
-    }
-
-    return -NME_UNSPEC;
-}
-
-/*****************************************************************************/
-
-static int
-routing_rule_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformRoutingRule *routing_rule)
-{
-    WaitForNlResponseResult      seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-    nm_auto_nlmsg struct nl_msg *msg        = NULL;
-    gs_free char *               errmsg     = NULL;
-    char                         s_buf[256];
-    int                          nle;
-
-    msg = _nl_msg_new_routing_rule(RTM_NEWRULE, flags, routing_rule);
-
-    event_handler_read_netlink(platform, FALSE);
-
-    nle = _nl_send_nlmsg(platform,
-                         msg,
-                         &seq_result,
-                         &errmsg,
-                         DELAYED_ACTION_RESPONSE_TYPE_VOID,
-                         NULL);
-    if (nle < 0) {
-        _LOGE("do-add-rule: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle);
-        return -NME_PL_NETLINK;
-    }
-
-    delayed_action_handle_all(platform, FALSE);
-
-    nm_assert(seq_result);
-
-    _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN,
-           "do-add-rule: %s",
-           wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)));
-
-    if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
-        return 0;
-    if (seq_result < 0)
-        return seq_result;
-    return -NME_UNSPEC;
-}
-
-/*****************************************************************************/
-
-static int
-qdisc_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformQdisc *qdisc)
-{
-    WaitForNlResponseResult      seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-    gs_free char *               errmsg     = NULL;
-    int                          nle;
-    char                         s_buf[256];
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-
-    /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind
-     * is undefined. */
-
-    msg = _nl_msg_new_qdisc(RTM_NEWQDISC, flags, qdisc);
-
-    event_handler_read_netlink(platform, FALSE);
-
-    nle = _nl_send_nlmsg(platform,
-                         msg,
-                         &seq_result,
-                         &errmsg,
-                         DELAYED_ACTION_RESPONSE_TYPE_VOID,
-                         NULL);
-    if (nle < 0) {
-        _LOGE("do-add-qdisc: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle);
-        return -NME_PL_NETLINK;
-    }
-
-    delayed_action_handle_all(platform, FALSE);
-
-    nm_assert(seq_result);
-
-    _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN,
-           "do-add-qdisc: %s",
-           wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)));
-
-    if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
-        return 0;
-    if (seq_result < 0)
-        return seq_result;
-    return -NME_UNSPEC;
-}
-
-/*****************************************************************************/
-
-static int
-tfilter_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformTfilter *tfilter)
-{
-    WaitForNlResponseResult      seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-    gs_free char *               errmsg     = NULL;
-    int                          nle;
-    char                         s_buf[256];
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-
-    /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind
-     * and tfilter.action.kind is undefined. */
-
-    msg = _nl_msg_new_tfilter(RTM_NEWTFILTER, flags, tfilter);
-
-    event_handler_read_netlink(platform, FALSE);
-
-    nle = _nl_send_nlmsg(platform,
-                         msg,
-                         &seq_result,
-                         &errmsg,
-                         DELAYED_ACTION_RESPONSE_TYPE_VOID,
-                         NULL);
-    if (nle < 0) {
-        _LOGE("do-add-tfilter: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle);
-        return -NME_PL_NETLINK;
-    }
-
-    delayed_action_handle_all(platform, FALSE);
-
-    nm_assert(seq_result);
-
-    _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN,
-           "do-add-tfilter: %s",
-           wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)));
-
-    if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
-        return 0;
-
-    return -NME_UNSPEC;
-}
-
-/*****************************************************************************/
-
-static gboolean
-event_handler(int fd, GIOCondition io_condition, gpointer user_data)
-{
-    delayed_action_handle_all(NM_PLATFORM(user_data), TRUE);
-    return TRUE;
-}
-
-/*****************************************************************************/
-
-/* copied from libnl3's recvmsgs() */
-static int
-event_handler_recvmsgs(NMPlatform *platform, gboolean handle_events)
-{
-    NMLinuxPlatformPrivate *    priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    struct nl_sock *            sk   = priv->nlh;
-    int                         n;
-    int                         err         = 0;
-    gboolean                    multipart   = 0;
-    gboolean                    interrupted = FALSE;
-    struct nlmsghdr *           hdr;
-    WaitForNlResponseResult     seq_result;
-    struct sockaddr_nl          nla = {0};
-    struct ucred                creds;
-    gboolean                    creds_has;
-    nm_auto_free unsigned char *buf = NULL;
-
-continue_reading:
-    nm_clear_pointer(&buf, free);
-    n = nl_recv(sk, &nla, &buf, &creds, &creds_has);
-
-    if (n <= 0) {
-        if (n == -NME_NL_MSG_TRUNC) {
-            int buf_size;
-
-            /* the message receive buffer was too small. We lost one message, which
-             * is unfortunate. Try to double the buffer size for the next time. */
-            buf_size = nl_socket_get_msg_buf_size(sk);
-            if (buf_size < 512 * 1024) {
-                buf_size *= 2;
-                _LOGT("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes",
-                      buf_size);
-                if (nl_socket_set_msg_buf_size(sk, buf_size) < 0)
-                    nm_assert_not_reached();
-                if (!handle_events)
-                    goto continue_reading;
-            }
-        }
-
-        return n;
-    }
-
-    hdr = (struct nlmsghdr *) buf;
-    while (nlmsg_ok(hdr, n)) {
-        nm_auto_nlmsg struct nl_msg *msg               = NULL;
-        gboolean                     abort_parsing     = FALSE;
-        gboolean                     process_valid_msg = FALSE;
-        guint32                      seq_number;
-        char                         buf_nlmsghdr[400];
-        const char *                 extack_msg = NULL;
-
-        msg = nlmsg_alloc_convert(hdr);
-
-        nlmsg_set_proto(msg, NETLINK_ROUTE);
-        nlmsg_set_src(msg, &nla);
-
-        if (!creds_has || creds.pid) {
-            if (!creds_has)
-                _LOGT("netlink: recvmsg: received message without credentials");
-            else
-                _LOGT("netlink: recvmsg: received non-kernel message (pid %d)", creds.pid);
-            err = 0;
-            goto stop;
-        }
-
-        _LOGt("netlink: recvmsg: new message %s",
-              nl_nlmsghdr_to_str(hdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)));
-
-        nlmsg_set_creds(msg, &creds);
-
-        if (hdr->nlmsg_flags & NLM_F_MULTI)
-            multipart = TRUE;
-
-        if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) {
-            /*
-             * We have to continue reading to clear
-             * all messages until a NLMSG_DONE is
-             * received and report the inconsistency.
-             */
-            interrupted = TRUE;
-        }
-
-        /* Other side wishes to see an ack for this message */
-        if (hdr->nlmsg_flags & NLM_F_ACK) {
-            /* FIXME: implement */
-        }
-
-        seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN;
-
-        if (hdr->nlmsg_type == NLMSG_DONE) {
-            /* messages terminates a multipart message, this is
-             * usually the end of a message and therefore we slip
-             * out of the loop by default. the user may overrule
-             * this action by skipping this packet. */
-            multipart  = FALSE;
-            seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
-        } else if (hdr->nlmsg_type == NLMSG_NOOP) {
-            /* Message to be ignored, the default action is to
-             * skip this message if no callback is specified. The
-             * user may overrule this action by returning
-             * NL_PROCEED. */
-        } else if (hdr->nlmsg_type == NLMSG_OVERRUN) {
-            /* Data got lost, report back to user. The default action is to
-             * quit parsing. The user may overrule this action by returning
-             * NL_SKIP or NL_PROCEED (dangerous) */
-            err           = -NME_NL_MSG_OVERFLOW;
-            abort_parsing = TRUE;
-        } else if (hdr->nlmsg_type == NLMSG_ERROR) {
-            /* Message carries a nlmsgerr */
-            struct nlmsgerr *e = nlmsg_data(hdr);
-
-            if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) {
-                /* Truncated error message, the default action
-                 * is to stop parsing. The user may overrule
-                 * this action by returning NL_SKIP or
-                 * NL_PROCEED (dangerous) */
-                err           = -NME_NL_MSG_TRUNC;
-                abort_parsing = TRUE;
-            } else if (e->error) {
-                int errsv = nm_errno_native(e->error);
-
-                if (NM_FLAGS_HAS(hdr->nlmsg_flags, NLM_F_ACK_TLVS)
-                    && hdr->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) {
-                    static const struct nla_policy policy[] = {
-                        [NLMSGERR_ATTR_MSG]  = {.type = NLA_STRING},
-                        [NLMSGERR_ATTR_OFFS] = {.type = NLA_U32},
-                    };
-                    struct nlattr *tb[G_N_ELEMENTS(policy)];
-                    struct nlattr *tlvs;
-
-                    tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len
-                                              - NLMSG_HDRLEN);
-                    if (nla_parse_arr(tb,
-                                      tlvs,
-                                      hdr->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len,
-                                      policy)
-                        >= 0) {
-                        if (tb[NLMSGERR_ATTR_MSG])
-                            extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]);
-                    }
-                }
-
-                /* Error message reported back from kernel. */
-                _LOGD("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d",
-                      nm_strerror_native(errsv),
-                      errsv,
-                      NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""),
-                      nlmsg_hdr(msg)->nlmsg_seq);
-                seq_result = -NM_ERRNO_NATIVE(errsv);
-            } else
-                seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
-        } else
-            process_valid_msg = TRUE;
-
-        seq_number = nlmsg_hdr(msg)->nlmsg_seq;
-
-        /* check whether the seq number is different from before, and
-         * whether the previous number (@nlh_seq_last_seen) is a pending
-         * refresh-all request. In that case, the pending request is thereby
-         * completed.
-         *
-         * We must do that before processing the message with event_valid_msg(),
-         * because we must track the completion of the pending request before that. */
-        event_seq_check_refresh_all(platform, seq_number);
-
-        if (process_valid_msg) {
-            /* Valid message (not checking for MULTIPART bit to
-             * get along with broken kernels. NL_SKIP has no
-             * effect on this.  */
-
-            event_valid_msg(platform, msg, handle_events);
-
-            seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
-        }
-
-        event_seq_check(platform, seq_number, seq_result, extack_msg);
-
-        if (abort_parsing)
-            goto stop;
-
-        err = 0;
-        hdr = nlmsg_next(hdr, &n);
-    }
-
-    if (multipart) {
-        /* Multipart message not yet complete, continue reading */
-        goto continue_reading;
-    }
-stop:
-    if (!handle_events) {
-        /* when we don't handle events, we want to drain all messages from the socket
-         * without handling the messages (but still check for sequence numbers).
-         * Repeat reading. */
-        goto continue_reading;
-    }
-
-    if (interrupted)
-        return -NME_NL_DUMP_INTR;
-    return err;
-}
-
-/*****************************************************************************/
-
-static gboolean
-event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks)
-{
-    nm_auto_pop_netns NMPNetns *netns = NULL;
-    NMLinuxPlatformPrivate *    priv  = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    int                         r;
-    struct pollfd               pfd;
-    gboolean                    any = FALSE;
-    int                         timeout_msec;
-    struct {
-        guint32 seq_number;
-        gint64  timeout_abs_ns;
-        gint64  now_ns;
-    } next;
-
-    if (!nm_platform_netns_push(platform, &netns)) {
-        delayed_action_wait_for_nl_response_complete_all(platform,
-                                                         WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS);
-        return FALSE;
-    }
-
-    for (;;) {
-        for (;;) {
-            int nle;
-
-            nle = event_handler_recvmsgs(platform, TRUE);
-
-            if (nle < 0) {
-                switch (nle) {
-                case -EAGAIN:
-                    goto after_read;
-                case -NME_NL_DUMP_INTR:
-                    _LOGD("netlink: read: uncritical failure to retrieve incoming events: %s (%d)",
-                          nm_strerror(nle),
-                          nle);
-                    break;
-                case -NME_NL_MSG_TRUNC:
-                case -ENOBUFS:
-                    _LOGI("netlink: read: %s. Need to resynchronize platform cache", ({
-                              const char *_reason = "unknown";
-                              switch (nle) {
-                              case -NME_NL_MSG_TRUNC:
-                                  _reason = "message truncated";
-                                  break;
-                              case -ENOBUFS:
-                                  _reason = "too many netlink events";
-                                  break;
-                              }
-                              _reason;
-                          }));
-                    event_handler_recvmsgs(platform, FALSE);
-                    delayed_action_wait_for_nl_response_complete_all(
-                        platform,
-                        WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC);
-
-                    delayed_action_schedule(platform,
-                                            DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS
-                                                | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES
-                                                | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES
-                                                | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
-                                                | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES
-                                                | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL
-                                                | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS
-                                                | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
-                                            NULL);
-                    break;
-                default:
-                    _LOGE("netlink: read: failed to retrieve incoming events: %s (%d)",
-                          nm_strerror(nle),
-                          nle);
-                    break;
-                }
-            }
-            any = TRUE;
-        }
-
-after_read:
-
-        if (!NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE))
-            return any;
-
-        delayed_action_wait_for_nl_response_complete_check(platform,
-                                                           WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN,
-                                                           &next.seq_number,
-                                                           &next.timeout_abs_ns,
-                                                           &next.now_ns);
-
-        if (!wait_for_acks
-            || !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE))
-            return any;
-
-        nm_assert(next.seq_number);
-        nm_assert(next.now_ns > 0);
-        nm_assert(next.timeout_abs_ns > next.now_ns);
-
-        _LOGT("netlink: read: wait for ACK for sequence number %u...", next.seq_number);
-
-        timeout_msec = (next.timeout_abs_ns - next.now_ns) / (NM_UTILS_NSEC_PER_SEC / 1000);
-
-        memset(&pfd, 0, sizeof(pfd));
-        pfd.fd     = nl_socket_get_fd(priv->nlh);
-        pfd.events = POLLIN;
-        r          = poll(&pfd, 1, MAX(1, timeout_msec));
-
-        if (r == 0) {
-            /* timeout and there is nothing to read. */
-            goto after_read;
-        }
-
-        if (r < 0) {
-            int errsv = errno;
-
-            if (errsv != EINTR) {
-                _LOGE("netlink: read: poll failed with %s", nm_strerror_native(errsv));
-                delayed_action_wait_for_nl_response_complete_all(
-                    platform,
-                    WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL);
-                return any;
-            }
-            /* Continue to read again, even if there might be nothing to read after EINTR. */
-        }
-    }
-}
-
-/*****************************************************************************/
-
-static void
-cache_update_link_udev(NMPlatform *platform, int ifindex, struct udev_device *udevice)
-{
-    nm_auto_nmpobj const NMPObject *obj_old = NULL;
-    nm_auto_nmpobj const NMPObject *obj_new = NULL;
-    NMPCacheOpsType                 cache_op;
-
-    cache_op = nmp_cache_update_link_udev(nm_platform_get_cache(platform),
-                                          ifindex,
-                                          udevice,
-                                          &obj_old,
-                                          &obj_new);
-
-    if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
-        nm_auto_pop_netns NMPNetns *netns = NULL;
-
-        cache_on_change(platform, cache_op, obj_old, obj_new);
-        if (!nm_platform_netns_push(platform, &netns))
-            return;
-        nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new);
-    }
-}
-
-static void
-udev_device_added(NMPlatform *platform, struct udev_device *udevice)
-{
-    const char *ifname;
-    const char *ifindex_s;
-    int         ifindex;
-
-    ifname = udev_device_get_sysname(udevice);
-    if (!ifname) {
-        _LOGD("udev-add: failed to get device's interface");
-        return;
-    }
-
-    ifindex_s = udev_device_get_property_value(udevice, "IFINDEX");
-    if (!ifindex_s) {
-        _LOGW("udev-add[%s]failed to get device's ifindex", ifname);
-        return;
-    }
-    ifindex = _nm_utils_ascii_str_to_int64(ifindex_s, 10, 1, G_MAXINT, 0);
-    if (ifindex <= 0) {
-        _LOGW("udev-add[%s]: retrieved invalid IFINDEX=%d", ifname, ifindex);
-        return;
-    }
-
-    if (!udev_device_get_syspath(udevice)) {
-        _LOGD("udev-add[%s,%d]: couldn't determine device path; ignoring...", ifname, ifindex);
-        return;
-    }
-
-    _LOGT("udev-add[%s,%d]: device added", ifname, ifindex);
-    cache_update_link_udev(platform, ifindex, udevice);
-}
-
-static gboolean
-_udev_device_removed_match_link(const NMPObject *obj, gpointer udevice)
-{
-    return obj->_link.udev.device == udevice;
-}
-
-static void
-udev_device_removed(NMPlatform *platform, struct udev_device *udevice)
-{
-    const char *ifindex_s;
-    int         ifindex = 0;
-
-    ifindex_s = udev_device_get_property_value(udevice, "IFINDEX");
-    ifindex   = _nm_utils_ascii_str_to_int64(ifindex_s, 10, 1, G_MAXINT, 0);
-    if (ifindex <= 0) {
-        const NMPObject *obj;
-
-        obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform),
-                                         0,
-                                         NULL,
-                                         FALSE,
-                                         NM_LINK_TYPE_NONE,
-                                         _udev_device_removed_match_link,
-                                         udevice);
-        if (obj)
-            ifindex = obj->link.ifindex;
-    }
-
-    _LOGD("udev-remove: IFINDEX=%d", ifindex);
-    if (ifindex <= 0)
-        return;
-
-    cache_update_link_udev(platform, ifindex, NULL);
-}
-
-static void
-handle_udev_event(NMUdevClient *udev_client, struct udev_device *udevice, gpointer user_data)
-{
-    nm_auto_pop_netns NMPNetns *netns    = NULL;
-    NMPlatform *                platform = NM_PLATFORM(user_data);
-    const char *                subsys;
-    const char *                ifindex;
-    guint64                     seqnum;
-    const char *                action;
-
-    action = udev_device_get_action(udevice);
-    g_return_if_fail(action);
-
-    subsys = udev_device_get_subsystem(udevice);
-    g_return_if_fail(nm_streq0(subsys, "net"));
-
-    if (!nm_platform_netns_push(platform, &netns))
-        return;
-
-    ifindex = udev_device_get_property_value(udevice, "IFINDEX");
-    seqnum  = udev_device_get_seqnum(udevice);
-    _LOGD("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT,
-          action,
-          subsys,
-          udev_device_get_sysname(udevice),
-          ifindex ?: "unknown",
-          seqnum);
-
-    if (NM_IN_STRSET(action, "add", "move"))
-        udev_device_added(platform, udevice);
-    else if (NM_IN_STRSET(action, "remove"))
-        udev_device_removed(platform, udevice);
-}
-
-/*****************************************************************************/
-
-void
-nm_linux_platform_setup(void)
-{
-    nm_platform_setup(nm_linux_platform_new(FALSE, FALSE));
-}
-
-/*****************************************************************************/
-
-static void
-nm_linux_platform_init(NMLinuxPlatform *self)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(self);
-
-    priv->delayed_action.list_master_connected = g_ptr_array_new();
-    priv->delayed_action.list_refresh_link     = g_ptr_array_new();
-    priv->delayed_action.list_wait_for_nl_response =
-        g_array_new(FALSE, TRUE, sizeof(DelayedActionWaitForNlResponseData));
-}
-
-static void
-constructed(GObject *_object)
-{
-    NMPlatform *            platform = NM_PLATFORM(_object);
-    NMLinuxPlatformPrivate *priv     = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-    int                     nle;
-    int                     fd;
-
-    nm_assert(!platform->_netns || platform->_netns == nmp_netns_get_current());
-
-    if (nm_platform_get_use_udev(platform)) {
-        priv->udev_client = nm_udev_client_new(NM_MAKE_STRV("net"), handle_udev_event, platform);
-    }
-
-    _LOGD("create (%s netns, %s, %s udev)",
-          !platform->_netns ? "ignore" : "use",
-          !platform->_netns && nmp_netns_is_initial()
-              ? "initial netns"
-              : (!nmp_netns_get_current()
-                     ? "no netns support"
-                     : nm_sprintf_bufa(100,
-                                       "in netns[%p]%s",
-                                       nmp_netns_get_current(),
-                                       nmp_netns_get_current() == nmp_netns_get_initial() ? "/main"
-                                                                                          : "")),
-          nm_platform_get_use_udev(platform) ? "use" : "no");
-
-    priv->genl = nl_socket_alloc();
-    g_assert(priv->genl);
-
-    nle = nl_connect(priv->genl, NETLINK_GENERIC);
-    if (nle) {
-        _LOGE("unable to connect the generic netlink socket \"%s\" (%d)", nm_strerror(nle), -nle);
-        nl_socket_free(priv->genl);
-        priv->genl = NULL;
-    }
-
-    priv->nlh = nl_socket_alloc();
-    g_assert(priv->nlh);
-
-    nle = nl_connect(priv->nlh, NETLINK_ROUTE);
-    g_assert(!nle);
-    nle = nl_socket_set_passcred(priv->nlh, 1);
-    g_assert(!nle);
-
-    /* No blocking for event socket, so that we can drain it safely. */
-    nle = nl_socket_set_nonblocking(priv->nlh);
-    g_assert(!nle);
-
-    /* use 8 MB for receive socket kernel queue. */
-    nle = nl_socket_set_buffer_size(priv->nlh, 8 * 1024 * 1024, 0);
-    g_assert(!nle);
-
-    nle = nl_socket_set_ext_ack(priv->nlh, TRUE);
-    if (nle)
-        _LOGD("could not enable extended acks on netlink socket");
-
-    /* explicitly set the msg buffer size and disable MSG_PEEK.
-     * If we later encounter NME_NL_MSG_TRUNC, we will adjust the buffer size. */
-    nl_socket_disable_msg_peek(priv->nlh);
-    nle = nl_socket_set_msg_buf_size(priv->nlh, 32 * 1024);
-    g_assert(!nle);
-
-    nle = nl_socket_add_memberships(priv->nlh,
-                                    RTNLGRP_IPV4_IFADDR,
-                                    RTNLGRP_IPV4_ROUTE,
-                                    RTNLGRP_IPV4_RULE,
-                                    RTNLGRP_IPV6_RULE,
-                                    RTNLGRP_IPV6_IFADDR,
-                                    RTNLGRP_IPV6_ROUTE,
-                                    RTNLGRP_LINK,
-                                    RTNLGRP_TC,
-                                    0);
-    g_assert(!nle);
-
-    fd = nl_socket_get_fd(priv->nlh);
-
-    _LOGD("Netlink socket for events established: port=%u, fd=%d",
-          nl_socket_get_local_port(priv->nlh),
-          fd);
-
-    priv->event_source =
-        nm_g_unix_fd_source_new(fd,
-                                G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP,
-                                G_PRIORITY_DEFAULT,
-                                event_handler,
-                                platform,
-                                NULL);
-    g_source_attach(priv->event_source, NULL);
-
-    /* complete construction of the GObject instance before populating the cache. */
-    G_OBJECT_CLASS(nm_linux_platform_parent_class)->constructed(_object);
-
-    _LOGD("populate platform cache");
-    delayed_action_schedule(
-        platform,
-        DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES
-            | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES
-            | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
-            | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES
-            | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL
-            | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
-        NULL);
-
-    delayed_action_handle_all(platform, FALSE);
-
-    /* Set up udev monitoring */
-    if (priv->udev_client) {
-        struct udev_enumerate * enumerator;
-        struct udev_list_entry *devices, *l;
-
-        /* And read initial device list */
-        enumerator = nm_udev_client_enumerate_new(priv->udev_client);
-
-        udev_enumerate_add_match_is_initialized(enumerator);
-
-        udev_enumerate_scan_devices(enumerator);
-
-        devices = udev_enumerate_get_list_entry(enumerator);
-        for (l = devices; l; l = udev_list_entry_get_next(l)) {
-            struct udev_device *udevice;
-
-            udevice = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerator),
-                                                   udev_list_entry_get_name(l));
-            if (!udevice)
-                continue;
-
-            udev_device_added(platform, udevice);
-            udev_device_unref(udevice);
-        }
-
-        udev_enumerate_unref(enumerator);
-    }
-}
-
-/* Similar to systemd's path_is_read_only_fs(), at
- * https://github.com/systemd/systemd/blob/v246/src/basic/stat-util.c#L132 */
-static int
-path_is_read_only_fs(const char *path)
-{
-    struct statvfs st;
-
-    if (statvfs(path, &st) < 0)
-        return -errno;
-
-    if (st.f_flag & ST_RDONLY)
-        return TRUE;
-
-    /* On NFS, statvfs() might not reflect whether we can actually
-	 * write to the remote share. Let's try again with
-	 * access(W_OK) which is more reliable, at least sometimes. */
-    if (access(path, W_OK) < 0 && errno == EROFS)
-        return TRUE;
-
-    return FALSE;
-}
-
-NMPlatform *
-nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support)
-{
-    gboolean use_udev = FALSE;
-
-    if (nmp_netns_is_initial() && path_is_read_only_fs("/sys") == FALSE)
-        use_udev = TRUE;
-
-    return g_object_new(NM_TYPE_LINUX_PLATFORM,
-                        NM_PLATFORM_LOG_WITH_PTR,
-                        log_with_ptr,
-                        NM_PLATFORM_USE_UDEV,
-                        use_udev,
-                        NM_PLATFORM_NETNS_SUPPORT,
-                        netns_support,
-                        NULL);
-}
-
-static void
-dispose(GObject *object)
-{
-    NMPlatform *            platform = NM_PLATFORM(object);
-    NMLinuxPlatformPrivate *priv     = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
-
-    _LOGD("dispose");
-
-    delayed_action_wait_for_nl_response_complete_all(platform,
-                                                     WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING);
-
-    priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE;
-    g_ptr_array_set_size(priv->delayed_action.list_master_connected, 0);
-    g_ptr_array_set_size(priv->delayed_action.list_refresh_link, 0);
-
-    G_OBJECT_CLASS(nm_linux_platform_parent_class)->dispose(object);
-}
-
-static void
-finalize(GObject *object)
-{
-    NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(object);
-
-    g_ptr_array_unref(priv->delayed_action.list_master_connected);
-    g_ptr_array_unref(priv->delayed_action.list_refresh_link);
-    g_array_unref(priv->delayed_action.list_wait_for_nl_response);
-
-    nl_socket_free(priv->genl);
-
-    nm_clear_g_source_inst(&priv->event_source);
-
-    nl_socket_free(priv->nlh);
-
-    if (priv->sysctl_get_prev_values) {
-        sysctl_clear_cache_list = g_slist_remove(sysctl_clear_cache_list, object);
-        g_hash_table_destroy(priv->sysctl_get_prev_values);
-    }
-
-    priv->udev_client = nm_udev_client_destroy(priv->udev_client);
-
-    G_OBJECT_CLASS(nm_linux_platform_parent_class)->finalize(object);
-}
-
-static void
-nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
-{
-    GObjectClass *   object_class   = G_OBJECT_CLASS(klass);
-    NMPlatformClass *platform_class = NM_PLATFORM_CLASS(klass);
-
-    object_class->constructed = constructed;
-    object_class->dispose     = dispose;
-    object_class->finalize    = finalize;
-
-    platform_class->sysctl_set       = sysctl_set;
-    platform_class->sysctl_set_async = sysctl_set_async;
-    platform_class->sysctl_get       = sysctl_get;
-
-    platform_class->link_add    = link_add;
-    platform_class->link_delete = link_delete;
-
-    platform_class->link_refresh = link_refresh;
-
-    platform_class->link_set_netns = link_set_netns;
-
-    platform_class->link_set_up    = link_set_up;
-    platform_class->link_set_down  = link_set_down;
-    platform_class->link_set_arp   = link_set_arp;
-    platform_class->link_set_noarp = link_set_noarp;
-
-    platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled;
-    platform_class->link_set_token               = link_set_token;
-
-    platform_class->link_set_address            = link_set_address;
-    platform_class->link_get_permanent_address  = link_get_permanent_address;
-    platform_class->link_set_mtu                = link_set_mtu;
-    platform_class->link_set_name               = link_set_name;
-    platform_class->link_set_sriov_params_async = link_set_sriov_params_async;
-    platform_class->link_set_sriov_vfs          = link_set_sriov_vfs;
-    platform_class->link_set_bridge_vlans       = link_set_bridge_vlans;
-
-    platform_class->link_get_physical_port_id = link_get_physical_port_id;
-    platform_class->link_get_dev_id           = link_get_dev_id;
-    platform_class->link_get_wake_on_lan      = link_get_wake_on_lan;
-    platform_class->link_get_driver_info      = link_get_driver_info;
-
-    platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
-    platform_class->link_supports_vlans          = link_supports_vlans;
-    platform_class->link_supports_sriov          = link_supports_sriov;
-
-    platform_class->link_enslave = link_enslave;
-    platform_class->link_release = link_release;
-
-    platform_class->link_can_assume = link_can_assume;
-
-    platform_class->link_vlan_change      = link_vlan_change;
-    platform_class->link_wireguard_change = link_wireguard_change;
-
-    platform_class->infiniband_partition_add    = infiniband_partition_add;
-    platform_class->infiniband_partition_delete = infiniband_partition_delete;
-
-    platform_class->wifi_get_capabilities            = wifi_get_capabilities;
-    platform_class->wifi_get_frequency               = wifi_get_frequency;
-    platform_class->wifi_get_station                 = wifi_get_station;
-    platform_class->wifi_get_mode                    = wifi_get_mode;
-    platform_class->wifi_set_mode                    = wifi_set_mode;
-    platform_class->wifi_set_powersave               = wifi_set_powersave;
-    platform_class->wifi_find_frequency              = wifi_find_frequency;
-    platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running;
-    platform_class->wifi_get_wake_on_wlan            = wifi_get_wake_on_wlan;
-    platform_class->wifi_set_wake_on_wlan            = wifi_set_wake_on_wlan;
-
-    platform_class->mesh_get_channel = mesh_get_channel;
-    platform_class->mesh_set_channel = mesh_set_channel;
-    platform_class->mesh_set_ssid    = mesh_set_ssid;
-
-    platform_class->wpan_get_pan_id     = wpan_get_pan_id;
-    platform_class->wpan_set_pan_id     = wpan_set_pan_id;
-    platform_class->wpan_get_short_addr = wpan_get_short_addr;
-    platform_class->wpan_set_short_addr = wpan_set_short_addr;
-    platform_class->wpan_set_channel    = wpan_set_channel;
-
-    platform_class->link_tun_add = link_tun_add;
-
-    platform_class->object_delete      = object_delete;
-    platform_class->ip4_address_add    = ip4_address_add;
-    platform_class->ip6_address_add    = ip6_address_add;
-    platform_class->ip4_address_delete = ip4_address_delete;
-    platform_class->ip6_address_delete = ip6_address_delete;
-
-    platform_class->ip_route_add = ip_route_add;
-    platform_class->ip_route_get = ip_route_get;
-
-    platform_class->routing_rule_add = routing_rule_add;
-
-    platform_class->qdisc_add   = qdisc_add;
-    platform_class->tfilter_add = tfilter_add;
-
-    platform_class->process_events = process_events;
-}
diff --git a/src/core/platform/nm-linux-platform.h b/src/core/platform/nm-linux-platform.h
deleted file mode 100644
index 6837e64a..00000000
--- a/src/core/platform/nm-linux-platform.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2012 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_LINUX_PLATFORM_H__
-#define __NETWORKMANAGER_LINUX_PLATFORM_H__
-
-#include "nm-platform.h"
-
-#define NM_TYPE_LINUX_PLATFORM (nm_linux_platform_get_type())
-#define NM_LINUX_PLATFORM(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform))
-#define NM_LINUX_PLATFORM_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass))
-#define NM_IS_LINUX_PLATFORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_LINUX_PLATFORM))
-#define NM_IS_LINUX_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_LINUX_PLATFORM))
-#define NM_LINUX_PLATFORM_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass))
-
-typedef struct _NMLinuxPlatform      NMLinuxPlatform;
-typedef struct _NMLinuxPlatformClass NMLinuxPlatformClass;
-
-GType nm_linux_platform_get_type(void);
-
-NMPlatform *nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support);
-
-void nm_linux_platform_setup(void);
-
-#endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */
diff --git a/src/core/platform/nm-platform-private.h b/src/core/platform/nm-platform-private.h
deleted file mode 100644
index cf805689..00000000
--- a/src/core/platform/nm-platform-private.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2017 Red Hat, Inc.
- */
-
-#ifndef __NM_PLATFORM_PRIVATE_H__
-#define __NM_PLATFORM_PRIVATE_H__
-
-#include "nm-platform.h"
-#include "nmp-object.h"
-
-NMPCache *nm_platform_get_cache(NMPlatform *self);
-
-#define NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform)                                          \
-    G_STMT_START                                                                               \
-    {                                                                                          \
-        NMPlatform *_platform = (platform);                                                    \
-                                                                                               \
-        nm_assert(NM_IS_PLATFORM(_platform));                                                  \
-        nm_assert(NM_IN_SET(nm_platform_netns_get(_platform), NULL, nmp_netns_get_current())); \
-    }                                                                                          \
-    G_STMT_END
-
-void nm_platform_cache_update_emit_signal(NMPlatform *     platform,
-                                          NMPCacheOpsType  cache_op,
-                                          const NMPObject *obj_old,
-                                          const NMPObject *obj_new);
-
-#endif /* __NM_PLATFORM_PRIVATE_H__ */
diff --git a/src/core/platform/nm-platform.c b/src/core/platform/nm-platform.c
deleted file mode 100644
index 0e5f8ab5..00000000
--- a/src/core/platform/nm-platform.c
+++ /dev/null
@@ -1,8966 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2012 - 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-platform.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <linux/fib_rules.h>
-#include <linux/ip.h>
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_tunnel.h>
-#include <linux/rtnetlink.h>
-#include <linux/tc_act/tc_mirred.h>
-#include <libudev.h>
-
-#include "nm-utils.h"
-#include "nm-core-internal.h"
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-udev-aux/nm-udev-utils.h"
-#include "nm-glib-aux/nm-secret-utils.h"
-
-#include "nm-core-utils.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "nm-platform-private.h"
-#include "nmp-object.h"
-#include "nm-platform/nmp-netns.h"
-
-/*****************************************************************************/
-
-G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPAddress, address_ptr)
-                == G_STRUCT_OFFSET(NMPlatformIP4Address, address));
-G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPAddress, address_ptr)
-                == G_STRUCT_OFFSET(NMPlatformIP6Address, address));
-G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPRoute, network_ptr)
-                == G_STRUCT_OFFSET(NMPlatformIP4Route, network));
-G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPRoute, network_ptr)
-                == G_STRUCT_OFFSET(NMPlatformIP6Route, network));
-
-G_STATIC_ASSERT(_nm_alignof(NMPlatformIPRoute) == _nm_alignof(NMPlatformIP4Route));
-G_STATIC_ASSERT(_nm_alignof(NMPlatformIPRoute) == _nm_alignof(NMPlatformIP6Route));
-G_STATIC_ASSERT(_nm_alignof(NMPlatformIPRoute) == _nm_alignof(NMPlatformIPXRoute));
-
-G_STATIC_ASSERT(_nm_alignof(NMPlatformIPAddress) == _nm_alignof(NMPlatformIP4Address));
-G_STATIC_ASSERT(_nm_alignof(NMPlatformIPAddress) == _nm_alignof(NMPlatformIP6Address));
-G_STATIC_ASSERT(_nm_alignof(NMPlatformIPAddress) == _nm_alignof(NMPlatformIPXAddress));
-
-/*****************************************************************************/
-
-G_STATIC_ASSERT(sizeof(((NMPLinkAddress *) NULL)->data) == NM_UTILS_HWADDR_LEN_MAX);
-G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_address.data) == NM_UTILS_HWADDR_LEN_MAX);
-G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == NM_UTILS_HWADDR_LEN_MAX);
-
-static const char *
-_nmp_link_address_to_string(const NMPLinkAddress *addr,
-                            char                  buf[static(NM_UTILS_HWADDR_LEN_MAX * 3)])
-{
-    nm_assert(addr);
-
-    if (addr->len > 0) {
-        if (!_nm_utils_hwaddr_ntoa(addr->data, addr->len, TRUE, buf, NM_UTILS_HWADDR_LEN_MAX * 3)) {
-            buf[0] = '\0';
-            g_return_val_if_reached(buf);
-        }
-    } else
-        buf[0] = '\0';
-
-    return buf;
-}
-
-gconstpointer
-nmp_link_address_get(const NMPLinkAddress *addr, size_t *length)
-{
-    if (!addr || addr->len <= 0) {
-        NM_SET_OUT(length, 0);
-        return NULL;
-    }
-
-    if (addr->len > NM_UTILS_HWADDR_LEN_MAX) {
-        NM_SET_OUT(length, 0);
-        g_return_val_if_reached(NULL);
-    }
-
-    NM_SET_OUT(length, addr->len);
-    return addr->data;
-}
-
-GBytes *
-nmp_link_address_get_as_bytes(const NMPLinkAddress *addr)
-{
-    gconstpointer data;
-    size_t        length;
-
-    data = nmp_link_address_get(addr, &length);
-
-    return length > 0 ? g_bytes_new(data, length) : NULL;
-}
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN      LOGD_PLATFORM
-#define _NMLOG_PREFIX_NAME "platform"
-
-#define NMLOG_COMMON(level, name, ...)                                                \
-    char                    __prefix[32];                                             \
-    const char *            __p_prefix = _NMLOG_PREFIX_NAME;                          \
-    const NMPlatform *const __self     = (self);                                      \
-    const char *            __name     = name;                                        \
-                                                                                      \
-    if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) {                    \
-        g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
-        __p_prefix = __prefix;                                                        \
-    }                                                                                 \
-    _nm_log(__level,                                                                  \
-            _NMLOG_DOMAIN,                                                            \
-            0,                                                                        \
-            __name,                                                                   \
-            NULL,                                                                     \
-            "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                          \
-            __p_prefix,                                                               \
-            NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") _NM_UTILS_MACRO_REST(__VA_ARGS__));
-
-#define _NMLOG(level, ...)                                \
-    G_STMT_START                                          \
-    {                                                     \
-        const NMLogLevel __level = (level);               \
-                                                          \
-        if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \
-            NMLOG_COMMON(level, NULL, __VA_ARGS__);       \
-        }                                                 \
-    }                                                     \
-    G_STMT_END
-
-#define _NMLOG2(level, ...)                               \
-    G_STMT_START                                          \
-    {                                                     \
-        const NMLogLevel __level = (level);               \
-                                                          \
-        if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \
-            NMLOG_COMMON(level, name, __VA_ARGS__);       \
-        }                                                 \
-    }                                                     \
-    G_STMT_END
-
-#define _NMLOG3(level, ...)                                                             \
-    G_STMT_START                                                                        \
-    {                                                                                   \
-        const NMLogLevel __level = (level);                                             \
-                                                                                        \
-        if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) {                               \
-            NMLOG_COMMON(level,                                                         \
-                         ifindex > 0 ? nm_platform_link_get_name(self, ifindex) : NULL, \
-                         __VA_ARGS__);                                                  \
-        }                                                                               \
-    }                                                                                   \
-    G_STMT_END
-
-/*****************************************************************************/
-
-static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = {0};
-
-enum {
-    PROP_0,
-    PROP_NETNS_SUPPORT,
-    PROP_USE_UDEV,
-    PROP_LOG_WITH_PTR,
-    LAST_PROP,
-};
-
-typedef struct _NMPlatformPrivate {
-    bool use_udev : 1;
-    bool log_with_ptr : 1;
-
-    guint              ip4_dev_route_blacklist_check_id;
-    guint              ip4_dev_route_blacklist_gc_timeout_id;
-    GHashTable *       ip4_dev_route_blacklist_hash;
-    NMDedupMultiIndex *multi_idx;
-    NMPCache *         cache;
-} NMPlatformPrivate;
-
-G_DEFINE_TYPE(NMPlatform, nm_platform, G_TYPE_OBJECT)
-
-#define NM_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMPlatform, NM_IS_PLATFORM)
-
-/*****************************************************************************/
-
-static void _ip4_dev_route_blacklist_schedule(NMPlatform *self);
-
-/*****************************************************************************/
-
-gboolean
-nm_platform_get_use_udev(NMPlatform *self)
-{
-    return NM_PLATFORM_GET_PRIVATE(self)->use_udev;
-}
-
-gboolean
-nm_platform_get_log_with_ptr(NMPlatform *self)
-{
-    return NM_PLATFORM_GET_PRIVATE(self)->log_with_ptr;
-}
-
-/*****************************************************************************/
-
-guint
-_nm_platform_signal_id_get(NMPlatformSignalIdType signal_type)
-{
-    nm_assert(signal_type > 0 && signal_type != NM_PLATFORM_SIGNAL_ID_NONE
-              && signal_type < _NM_PLATFORM_SIGNAL_ID_LAST);
-
-    return signals[signal_type];
-}
-
-/*****************************************************************************/
-
-/* Singleton NMPlatform subclass instance and cached class object */
-NM_DEFINE_SINGLETON_INSTANCE(NMPlatform);
-
-NM_DEFINE_SINGLETON_REGISTER(NMPlatform);
-
-/* Just always initialize a @klass instance. NM_PLATFORM_GET_CLASS()
- * is only a plain read on the self instance, which the compiler
- * like can optimize out.
- */
-#define _CHECK_SELF_VOID(self, klass)           \
-    NMPlatformClass *klass;                     \
-    do {                                        \
-        g_return_if_fail(NM_IS_PLATFORM(self)); \
-        klass = NM_PLATFORM_GET_CLASS(self);    \
-        (void) klass;                           \
-    } while (0)
-
-#define _CHECK_SELF(self, klass, err_val)                    \
-    NMPlatformClass *klass;                                  \
-    do {                                                     \
-        g_return_val_if_fail(NM_IS_PLATFORM(self), err_val); \
-        klass = NM_PLATFORM_GET_CLASS(self);                 \
-        (void) klass;                                        \
-    } while (0)
-
-#define _CHECK_SELF_NETNS(self, klass, netns, err_val)       \
-    nm_auto_pop_netns NMPNetns *netns = NULL;                \
-    NMPlatformClass *           klass;                       \
-    do {                                                     \
-        g_return_val_if_fail(NM_IS_PLATFORM(self), err_val); \
-        klass = NM_PLATFORM_GET_CLASS(self);                 \
-        (void) klass;                                        \
-        if (!nm_platform_netns_push(self, &netns))           \
-            return (err_val);                                \
-    } while (0)
-
-/**
- * nm_platform_setup:
- * @instance: the #NMPlatform instance
- *
- * Failing to set up #NMPlatform singleton results in a fatal error,
- * as well as trying to initialize it multiple times without freeing
- * it.
- *
- * NetworkManager will typically use only one platform object during
- * its run. Test programs might want to switch platform implementations,
- * though.
- */
-void
-nm_platform_setup(NMPlatform *instance)
-{
-    g_return_if_fail(NM_IS_PLATFORM(instance));
-    g_return_if_fail(!singleton_instance);
-
-    singleton_instance = instance;
-
-    nm_singleton_instance_register();
-
-    nm_log_dbg(LOGD_CORE,
-               "setup %s singleton (" NM_HASH_OBFUSCATE_PTR_FMT ")",
-               "NMPlatform",
-               NM_HASH_OBFUSCATE_PTR(instance));
-}
-
-/**
- * nm_platform_get:
- * @self: platform instance
- *
- * Retrieve #NMPlatform singleton. Use this whenever you want to connect to
- * #NMPlatform signals. It is an error to call it before nm_platform_setup().
- *
- * Returns: (transfer none): The #NMPlatform singleton reference.
- */
-NMPlatform *
-nm_platform_get()
-{
-    g_assert(singleton_instance);
-
-    return singleton_instance;
-}
-
-/*****************************************************************************/
-
-NMDedupMultiIndex *
-nm_platform_get_multi_idx(NMPlatform *self)
-{
-    g_return_val_if_fail(NM_IS_PLATFORM(self), NULL);
-
-    return NM_PLATFORM_GET_PRIVATE(self)->multi_idx;
-}
-
-/*****************************************************************************/
-
-static NM_UTILS_LOOKUP_STR_DEFINE(
-    _nmp_nlm_flag_to_string_lookup,
-    NMPNlmFlags,
-    NM_UTILS_LOOKUP_DEFAULT(NULL),
-    NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_ADD, "add"),
-    NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_CHANGE, "change"),
-    NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_REPLACE, "replace"),
-    NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_PREPEND, "prepend"),
-    NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_APPEND, "append"),
-    NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_TEST, "test"),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_F_APPEND),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_FMASK),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE),
-    NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_F_ECHO), );
-
-#define _nmp_nlm_flag_to_string(flags)                               \
-    ({                                                               \
-        NMPNlmFlags _flags = (flags);                                \
-                                                                     \
-        _nmp_nlm_flag_to_string_lookup(flags)                        \
-            ?: nm_sprintf_bufa(100, "new[0x%x]", (unsigned) _flags); \
-    })
-
-/*****************************************************************************/
-
-volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPORT_NUM] = {};
-
-static const struct {
-    bool        compile_time_default;
-    const char *name;
-    const char *desc;
-} _nm_platform_kernel_support_info[_NM_PLATFORM_KERNEL_SUPPORT_NUM] = {
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS] =
-        {
-            .compile_time_default = TRUE,
-            .name                 = "EXTENDED_IFA_FLAGS",
-            .desc                 = "IPv6 temporary addresses support",
-        },
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL] =
-        {
-            .compile_time_default = TRUE,
-            .name                 = "USER_IPV6LL",
-            .desc                 = "IFLA_INET6_ADDR_GEN_MODE support",
-        },
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF] =
-        {
-            .compile_time_default = (RTA_MAX >= 20 /* RTA_PREF */),
-            .name                 = "RTA_PREF",
-            .desc                 = "ability to set router preference for IPv6 routes",
-        },
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV] =
-        {
-            .compile_time_default = (FRA_MAX >= 19 /* FRA_L3MDEV */),
-            .name                 = "FRA_L3MDEV",
-            .desc                 = "FRA_L3MDEV attribute for policy routing rules",
-        },
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE] =
-        {
-            .compile_time_default = (FRA_MAX >= 20 /* FRA_UID_RANGE */),
-            .name                 = "FRA_UID_RANGE",
-            .desc                 = "FRA_UID_RANGE attribute for policy routing rules",
-        },
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL] =
-        {
-            .compile_time_default = (FRA_MAX >= 21 /* FRA_PROTOCOL */),
-            .name                 = "FRA_PROTOCOL",
-            .desc                 = "FRA_PROTOCOL attribute for policy routing rules",
-        },
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO] =
-        {
-            .compile_time_default = (FRA_MAX >= 22 /* FRA_IP_PROTO */),
-            .name                 = "FRA_IP_PROTO",
-            .desc = "FRA_IP_PROTO, FRA_SPORT_RANGE, FRA_DPORT_RANGE attributes for policy routing "
-                    "rules",
-        },
-    [NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED] =
-        {
-            .compile_time_default = (IFLA_BR_MAX >= 41 /* IFLA_BR_VLAN_STATS_ENABLED */),
-            .name                 = "IFLA_BR_VLAN_STATS_ENABLE",
-            .desc                 = "IFLA_BR_VLAN_STATS_ENABLE bridge link attribute",
-        },
-};
-
-int
-_nm_platform_kernel_support_init(NMPlatformKernelSupportType type, int value)
-{
-    volatile int *p_state;
-    gboolean      set_default = FALSE;
-
-    nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < G_N_ELEMENTS(_nm_platform_kernel_support_state));
-
-    p_state = &_nm_platform_kernel_support_state[type];
-
-    if (value == 0) {
-        set_default = TRUE;
-        value       = _nm_platform_kernel_support_info[type].compile_time_default ? 1 : -1;
-    }
-
-    nm_assert(NM_IN_SET(value, -1, 1));
-
-    if (!g_atomic_int_compare_and_exchange(p_state, 0, value)) {
-        value = g_atomic_int_get(p_state);
-        nm_assert(NM_IN_SET(value, -1, 1));
-        return value;
-    }
-
-#undef NM_THREAD_SAFE_ON_MAIN_THREAD
-#define NM_THREAD_SAFE_ON_MAIN_THREAD 0
-
-    if (set_default) {
-        nm_log_dbg(LOGD_PLATFORM,
-                   "platform: kernel-support for %s (%s) not detected: assume %ssupported",
-                   _nm_platform_kernel_support_info[type].name,
-                   _nm_platform_kernel_support_info[type].desc,
-                   value >= 0 ? "" : "not ");
-    } else {
-        nm_log_dbg(LOGD_PLATFORM,
-                   "platform: kernel-support for %s (%s) detected: %ssupported",
-                   _nm_platform_kernel_support_info[type].name,
-                   _nm_platform_kernel_support_info[type].desc,
-                   value >= 0 ? "" : "not ");
-    }
-
-#undef NM_THREAD_SAFE_ON_MAIN_THREAD
-#define NM_THREAD_SAFE_ON_MAIN_THREAD 1
-
-    return value;
-}
-
-/*****************************************************************************/
-
-/**
- * nm_platform_process_events:
- * @self: platform instance
- *
- * Process pending events or handle pending delayed-actions.
- * Effectively, this reads the netlink socket and processes
- * new netlink messages. Possibly it will raise change signals.
- */
-void
-nm_platform_process_events(NMPlatform *self)
-{
-    _CHECK_SELF_VOID(self, klass);
-
-    if (klass->process_events)
-        klass->process_events(self);
-}
-
-const NMPlatformLink *
-nm_platform_process_events_ensure_link(NMPlatform *self, int ifindex, const char *ifname)
-{
-    const NMPObject *obj;
-    gboolean         refreshed = FALSE;
-
-    g_return_val_if_fail(NM_IS_PLATFORM(self), NULL);
-
-    if (ifindex <= 0 && !ifname)
-        return NULL;
-
-    /* we look into the cache, whether a link for given ifindex/ifname
-     * exits. If not, we poll the netlink socket, maybe the event
-     * with the link is waiting.
-     *
-     * Then we try again to find the object.
-     *
-     * If the link is already cached the first time, we avoid polling
-     * the netlink socket. */
-again:
-    obj = nmp_cache_lookup_link_full(
-        nm_platform_get_cache(self),
-        ifindex,
-        ifname,
-        FALSE, /* also invisible. We don't care here whether udev is ready */
-        NM_LINK_TYPE_NONE,
-        NULL,
-        NULL);
-    if (obj)
-        return NMP_OBJECT_CAST_LINK(obj);
-    if (!refreshed) {
-        refreshed = TRUE;
-        nm_platform_process_events(self);
-        goto again;
-    }
-
-    return NULL;
-}
-
-/*****************************************************************************/
-
-/**
- * nm_platform_sysctl_open_netdir:
- * @self: platform instance
- * @ifindex: the ifindex for which to open /sys/class/net/%s
- * @out_ifname: optional output argument of the found ifname.
- *
- * Wraps nmp_utils_sysctl_open_netdir() by first changing into the right
- * network-namespace.
- *
- * Returns: on success, the open file descriptor to the /sys/class/net/%s
- *   directory.
- */
-int
-nm_platform_sysctl_open_netdir(NMPlatform *self, int ifindex, char *out_ifname)
-{
-    const char *ifname_guess;
-    _CHECK_SELF_NETNS(self, klass, netns, -1);
-
-    g_return_val_if_fail(ifindex > 0, -1);
-
-    /* we don't have an @ifname_guess argument to make the API nicer.
-     * But still do a cache-lookup first. Chances are good that we have
-     * the right ifname cached and save if_indextoname() */
-    ifname_guess = nm_platform_link_get_name(self, ifindex);
-
-    return nmp_utils_sysctl_open_netdir(ifindex, ifname_guess, out_ifname);
-}
-
-/**
- * nm_platform_sysctl_set:
- * @self: platform instance
- * @pathid: if @dirfd is present, this must be the full path that is looked up.
- *   It is required for logging.
- * @dirfd: optional file descriptor for parent directory for openat()
- * @path: Absolute option path
- * @value: Value to write
- *
- * This function is intended to be used for writing values to sysctl-style
- * virtual runtime configuration files. This includes not only /proc/sys
- * but also for example /sys/class.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-nm_platform_sysctl_set(NMPlatform *self,
-                       const char *pathid,
-                       int         dirfd,
-                       const char *path,
-                       const char *value)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(path, FALSE);
-    g_return_val_if_fail(value, FALSE);
-
-    return klass->sysctl_set(self, pathid, dirfd, path, value);
-}
-
-/**
- * nm_platform_sysctl_set_async:
- * @self: platform instance
- * @pathid: if @dirfd is present, this must be the full path that is looked up
- * @dirfd: optional file descriptor for parent directory for openat()
- * @path: absolute option path
- * @values: NULL-terminated array of strings to be written
- * @callback: function called on termination
- * @data: data passed to callback function
- * @cancellable: to cancel the operation
- *
- * This function is intended to be used for writing values to sysctl-style
- * virtual runtime configuration files. This includes not only /proc/sys
- * but also for example /sys/class. The function does not block and returns
- * immediately. The callback is always invoked, and asynchronously. The file
- * is closed after writing each value and reopened to write the next one so
- * that the function can be used safely on all /proc and /sys files,
- * independently of how /proc/sys/kernel/sysctl_writes_strict is configured.
- */
-void
-nm_platform_sysctl_set_async(NMPlatform *            self,
-                             const char *            pathid,
-                             int                     dirfd,
-                             const char *            path,
-                             const char *const *     values,
-                             NMPlatformAsyncCallback callback,
-                             gpointer                data,
-                             GCancellable *          cancellable)
-{
-    _CHECK_SELF_VOID(self, klass);
-
-    klass->sysctl_set_async(self, pathid, dirfd, path, values, callback, data, cancellable);
-}
-
-gboolean
-nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(NMPlatform *self, const char *iface, int value)
-{
-    const char *path;
-    gint64      cur;
-    char        buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    /* the hop-limit provided via RA is uint8. */
-    if (value > 0xFF)
-        return FALSE;
-
-    /* don't allow unreasonable small values */
-    if (value < 10)
-        return FALSE;
-
-    path = nm_utils_sysctl_ip_conf_path(AF_INET6, buf, iface, "hop_limit");
-    cur  = nm_platform_sysctl_get_int_checked(self,
-                                             NMP_SYSCTL_PATHID_ABSOLUTE(path),
-                                             10,
-                                             1,
-                                             G_MAXINT32,
-                                             -1);
-
-    /* only allow increasing the hop-limit to avoid DOS by an attacker
-     * setting a low hop-limit (CVE-2015-2924, rh#1209902) */
-
-    if (value < cur)
-        return FALSE;
-    if (value != cur) {
-        char svalue[20];
-
-        sprintf(svalue, "%d", value);
-        nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), svalue);
-    }
-
-    return TRUE;
-}
-
-gboolean
-nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(NMPlatform *self,
-                                                    const char *iface,
-                                                    guint       value_ms)
-{
-    char  path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
-    char  str[128];
-    guint clamped;
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    if (!value_ms)
-        return TRUE;
-
-    /* RFC 4861 says the value can't be greater than one hour.
-     * Also use a reasonable lower threshold. */
-    clamped = NM_CLAMP(value_ms, 100, 3600000);
-    nm_sprintf_buf(path, "/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms", iface);
-    nm_sprintf_buf(str, "%u", clamped);
-    if (!nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), str))
-        return FALSE;
-
-    /* Set stale time in the same way as kernel */
-    nm_sprintf_buf(path, "/proc/sys/net/ipv6/neigh/%s/gc_stale_time", iface);
-    nm_sprintf_buf(str, "%u", clamped * 3 / 1000);
-
-    return nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), str);
-}
-
-gboolean
-nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(NMPlatform *self,
-                                                  const char *iface,
-                                                  guint       value_ms)
-{
-    char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
-    char str[128];
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    if (!value_ms)
-        return TRUE;
-
-    nm_sprintf_buf(path, "/proc/sys/net/ipv6/neigh/%s/retrans_time_ms", iface);
-    nm_sprintf_buf(str, "%u", NM_CLAMP(value_ms, 10, 3600000));
-
-    return nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), str);
-}
-
-/**
- * nm_platform_sysctl_get:
- * @self: platform instance
- * @dirfd: if non-negative, used to lookup the path via openat().
- * @pathid: if @dirfd is present, this must be the full path that is looked up.
- *   It is required for logging.
- * @path: Absolute path to sysctl
- *
- * Returns: (transfer full): Contents of the virtual sysctl file.
- *
- * If the path does not exist, %NULL is returned and %errno set to %ENOENT.
- */
-char *
-nm_platform_sysctl_get(NMPlatform *self, const char *pathid, int dirfd, const char *path)
-{
-    _CHECK_SELF(self, klass, NULL);
-
-    g_return_val_if_fail(path, NULL);
-
-    return klass->sysctl_get(self, pathid, dirfd, path);
-}
-
-/**
- * nm_platform_sysctl_get_int32:
- * @self: platform instance
- * @pathid: if @dirfd is present, this must be the full path that is looked up.
- *   It is required for logging.
- * @dirfd: if non-negative, used to lookup the path via openat().
- * @path: Absolute path to sysctl
- * @fallback: default value, if the content of path could not be read
- * as decimal integer.
- *
- * Returns: contents of the sysctl file parsed as s32 integer, or
- * @fallback on error. On error, %errno will be set to a non-zero
- * value, on success %errno will be set to zero.
- */
-gint32
-nm_platform_sysctl_get_int32(NMPlatform *self,
-                             const char *pathid,
-                             int         dirfd,
-                             const char *path,
-                             gint32      fallback)
-{
-    return nm_platform_sysctl_get_int_checked(self,
-                                              pathid,
-                                              dirfd,
-                                              path,
-                                              10,
-                                              G_MININT32,
-                                              G_MAXINT32,
-                                              fallback);
-}
-
-/**
- * nm_platform_sysctl_get_int_checked:
- * @self: platform instance
- * @pathid: if @dirfd is present, this must be the full path that is looked up.
- *   It is required for logging.
- * @dirfd: if non-negative, used to lookup the path via openat().
- * @path: Absolute path to sysctl
- * @base: base of numeric conversion
- * @min: minimal value that is still valid
- * @max: maximal value that is still valid
- * @fallback: default value, if the content of path could not be read
- * as valid integer.
- *
- * Returns: contents of the sysctl file parsed as s64 integer, or
- * @fallback on error. On error, %errno will be set to a non-zero
- * value. On success, %errno will be set to zero. The returned value
- * will always be in the range between @min and @max
- * (inclusive) or @fallback.
- * If the file does not exist, the fallback is returned and %errno
- * is set to ENOENT.
- */
-gint64
-nm_platform_sysctl_get_int_checked(NMPlatform *self,
-                                   const char *pathid,
-                                   int         dirfd,
-                                   const char *path,
-                                   guint       base,
-                                   gint64      min,
-                                   gint64      max,
-                                   gint64      fallback)
-{
-    char * value = NULL;
-    gint32 ret;
-    int    errsv;
-
-    _CHECK_SELF(self, klass, fallback);
-
-    g_return_val_if_fail(path, fallback);
-
-    if (!path) {
-        errno = EINVAL;
-        return fallback;
-    }
-
-    value = nm_platform_sysctl_get(self, pathid, dirfd, path);
-    if (!value) {
-        /* nm_platform_sysctl_get() set errno to ENOENT if the file does not exist.
-         * Propagate/preserve that. */
-        if (errno != ENOENT)
-            errno = EINVAL;
-        return fallback;
-    }
-
-    ret   = _nm_utils_ascii_str_to_int64(value, base, min, max, fallback);
-    errsv = errno;
-    g_free(value);
-    errno = errsv;
-    return ret;
-}
-
-/*****************************************************************************/
-
-char *
-nm_platform_sysctl_ip_conf_get(NMPlatform *self,
-                               int         addr_family,
-                               const char *ifname,
-                               const char *property)
-{
-    char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
-
-    return nm_platform_sysctl_get(
-        self,
-        NMP_SYSCTL_PATHID_ABSOLUTE(
-            nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property)));
-}
-
-gint64
-nm_platform_sysctl_ip_conf_get_int_checked(NMPlatform *self,
-                                           int         addr_family,
-                                           const char *ifname,
-                                           const char *property,
-                                           guint       base,
-                                           gint64      min,
-                                           gint64      max,
-                                           gint64      fallback)
-{
-    char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
-
-    return nm_platform_sysctl_get_int_checked(
-        self,
-        NMP_SYSCTL_PATHID_ABSOLUTE(
-            nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property)),
-        base,
-        min,
-        max,
-        fallback);
-}
-
-gboolean
-nm_platform_sysctl_ip_conf_set(NMPlatform *self,
-                               int         addr_family,
-                               const char *ifname,
-                               const char *property,
-                               const char *value)
-{
-    char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
-
-    return nm_platform_sysctl_set(
-        self,
-        NMP_SYSCTL_PATHID_ABSOLUTE(
-            nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property)),
-        value);
-}
-
-gboolean
-nm_platform_sysctl_ip_conf_set_int64(NMPlatform *self,
-                                     int         addr_family,
-                                     const char *ifname,
-                                     const char *property,
-                                     gint64      value)
-{
-    char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
-    char s[64];
-
-    return nm_platform_sysctl_set(
-        self,
-        NMP_SYSCTL_PATHID_ABSOLUTE(
-            nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property)),
-        nm_sprintf_buf(s, "%" G_GINT64_FORMAT, value));
-}
-
-int
-nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(NMPlatform *self,
-                                              const char *ifname,
-                                              gboolean    consider_all,
-                                              gboolean *  out_due_to_all)
-{
-    int val, val_all;
-
-    NM_SET_OUT(out_due_to_all, FALSE);
-
-    if (!ifname)
-        return -1;
-
-    val = nm_platform_sysctl_ip_conf_get_int_checked(self,
-                                                     AF_INET,
-                                                     ifname,
-                                                     "rp_filter",
-                                                     10,
-                                                     0,
-                                                     2,
-                                                     -1);
-    if (val == -1)
-        return -1;
-
-    /* the effectively used value is the rp_filter sysctl value of MAX(all,ifname).
-     * Note that this is the numerical MAX(), despite rp_filter "1" being more strict
-     * than "2". */
-    if (val < 2 && consider_all && !nm_streq(ifname, "all")) {
-        val_all = nm_platform_sysctl_ip_conf_get_int_checked(self,
-                                                             AF_INET,
-                                                             "all",
-                                                             "rp_filter",
-                                                             10,
-                                                             0,
-                                                             2,
-                                                             val);
-        if (val_all > val) {
-            val = val_all;
-            NM_SET_OUT(out_due_to_all, TRUE);
-        }
-    }
-
-    return val;
-}
-
-/*****************************************************************************/
-
-static int
-_link_get_all_presort(gconstpointer p_a, gconstpointer p_b, gpointer sort_by_name)
-{
-    const NMPlatformLink *a = NMP_OBJECT_CAST_LINK(*((const NMPObject **) p_a));
-    const NMPlatformLink *b = NMP_OBJECT_CAST_LINK(*((const NMPObject **) p_b));
-
-    /* Loopback always first */
-    if (a->ifindex == 1)
-        return -1;
-    if (b->ifindex == 1)
-        return 1;
-
-    if (GPOINTER_TO_INT(sort_by_name)) {
-        /* Initialized links first */
-        if (a->initialized > b->initialized)
-            return -1;
-        if (a->initialized < b->initialized)
-            return 1;
-
-        return strcmp(a->name, b->name);
-    } else
-        return a->ifindex - b->ifindex;
-}
-
-/**
- * nm_platform_link_get_all:
- * @self: platform instance
- * @sort_by_name: whether to sort by name or ifindex.
- *
- * Retrieve a snapshot of configuration for all links at once. The result is
- * owned by the caller and should be freed with g_ptr_array_unref().
- */
-GPtrArray *
-nm_platform_link_get_all(NMPlatform *self, gboolean sort_by_name)
-{
-    gs_unref_ptrarray GPtrArray *links = NULL;
-    GPtrArray *                  result;
-    guint                        i, nresult;
-    gs_unref_hashtable GHashTable *unseen = NULL;
-    const NMPlatformLink *         item;
-    NMPLookup                      lookup;
-
-    _CHECK_SELF(self, klass, NULL);
-
-    nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK);
-    links = nm_dedup_multi_objs_to_ptr_array_head(nm_platform_lookup(self, &lookup), NULL, NULL);
-    if (!links)
-        return NULL;
-
-    for (i = 0; i < links->len;) {
-        if (!nmp_object_is_visible(links->pdata[i]))
-            g_ptr_array_remove_index_fast(links, i);
-        else
-            i++;
-    }
-
-    if (links->len == 0)
-        return NULL;
-
-    /* first sort the links by their ifindex or name. Below we will sort
-     * further by moving children/slaves to the end. */
-    g_ptr_array_sort_with_data(links, _link_get_all_presort, GINT_TO_POINTER(sort_by_name));
-
-    unseen = g_hash_table_new(nm_direct_hash, NULL);
-    for (i = 0; i < links->len; i++) {
-        item = NMP_OBJECT_CAST_LINK(links->pdata[i]);
-        nm_assert(item->ifindex > 0);
-        if (!g_hash_table_insert(unseen, GINT_TO_POINTER(item->ifindex), NULL))
-            nm_assert_not_reached();
-    }
-
-#if NM_MORE_ASSERTS
-    /* Ensure that link_get_all returns a consistent and valid result. */
-    for (i = 0; i < links->len; i++) {
-        item = NMP_OBJECT_CAST_LINK(links->pdata[i]);
-
-        if (!item->ifindex)
-            continue;
-        if (item->master != 0) {
-            g_warn_if_fail(item->master > 0);
-            g_warn_if_fail(item->master != item->ifindex);
-            g_warn_if_fail(g_hash_table_contains(unseen, GINT_TO_POINTER(item->master)));
-        }
-        if (item->parent != 0) {
-            if (item->parent != NM_PLATFORM_LINK_OTHER_NETNS) {
-                g_warn_if_fail(item->parent > 0);
-                g_warn_if_fail(item->parent != item->ifindex);
-                g_warn_if_fail(g_hash_table_contains(unseen, GINT_TO_POINTER(item->parent)));
-            }
-        }
-    }
-#endif
-
-    /* Re-order the links list such that children/slaves come after all ancestors */
-    nm_assert(g_hash_table_size(unseen) == links->len);
-    nresult = links->len;
-    result  = g_ptr_array_new_full(nresult, (GDestroyNotify) nmp_object_unref);
-
-    while (TRUE) {
-        gboolean found_something = FALSE;
-        guint    first_idx       = G_MAXUINT;
-
-        for (i = 0; i < links->len; i++) {
-            item = NMP_OBJECT_CAST_LINK(links->pdata[i]);
-
-            if (!item)
-                continue;
-
-            g_assert(g_hash_table_contains(unseen, GINT_TO_POINTER(item->ifindex)));
-
-            if (item->master > 0 && g_hash_table_contains(unseen, GINT_TO_POINTER(item->master)))
-                goto skip;
-            if (item->parent > 0 && g_hash_table_contains(unseen, GINT_TO_POINTER(item->parent)))
-                goto skip;
-
-            g_hash_table_remove(unseen, GINT_TO_POINTER(item->ifindex));
-            g_ptr_array_add(result, links->pdata[i]);
-            links->pdata[i] = NULL;
-            found_something = TRUE;
-            continue;
-skip:
-            if (first_idx == G_MAXUINT)
-                first_idx = i;
-        }
-
-        if (found_something) {
-            if (first_idx == G_MAXUINT)
-                break;
-        } else {
-            nm_assert(first_idx != G_MAXUINT);
-            /* There is a loop, pop the first (remaining) element from the list.
-             * This can happen for veth pairs where each peer is parent of the other end. */
-            item = NMP_OBJECT_CAST_LINK(links->pdata[first_idx]);
-            nm_assert(item);
-            g_hash_table_remove(unseen, GINT_TO_POINTER(item->ifindex));
-            g_ptr_array_add(result, links->pdata[first_idx]);
-            links->pdata[first_idx] = NULL;
-        }
-        nm_assert(result->len < nresult);
-    }
-    nm_assert(result->len == nresult);
-
-    return result;
-}
-
-/*****************************************************************************/
-
-const NMPObject *
-nm_platform_link_get_obj(NMPlatform *self, int ifindex, gboolean visible_only)
-{
-    const NMPObject *obj_cache;
-
-    _CHECK_SELF(self, klass, NULL);
-
-    obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(self), ifindex);
-    if (!obj_cache || (visible_only && !nmp_object_is_visible(obj_cache)))
-        return NULL;
-    return obj_cache;
-}
-
-/*****************************************************************************/
-
-/**
- * nm_platform_link_get:
- * @self: platform instance
- * @ifindex: ifindex of the link
- *
- * Lookup the internal NMPlatformLink object.
- *
- * Returns: %NULL, if such a link exists or the internal
- * platform link object. Do not modify the returned value.
- * Also, be aware that any subsequent platform call might
- * invalidate/modify the returned instance.
- **/
-const NMPlatformLink *
-nm_platform_link_get(NMPlatform *self, int ifindex)
-{
-    return NMP_OBJECT_CAST_LINK(nm_platform_link_get_obj(self, ifindex, TRUE));
-}
-
-/**
- * nm_platform_link_get_by_ifname:
- * @self: platform instance
- * @ifname: the ifname
- *
- * Returns: the first #NMPlatformLink instance with the given name.
- **/
-const NMPlatformLink *
-nm_platform_link_get_by_ifname(NMPlatform *self, const char *ifname)
-{
-    const NMPObject *obj;
-
-    _CHECK_SELF(self, klass, NULL);
-
-    if (!ifname || !*ifname)
-        return NULL;
-
-    obj = nmp_cache_lookup_link_full(nm_platform_get_cache(self),
-                                     0,
-                                     ifname,
-                                     TRUE,
-                                     NM_LINK_TYPE_NONE,
-                                     NULL,
-                                     NULL);
-    return NMP_OBJECT_CAST_LINK(obj);
-}
-
-struct _nm_platform_link_get_by_address_data {
-    gconstpointer data;
-    guint8        len;
-};
-
-static gboolean
-_nm_platform_link_get_by_address_match_link(const NMPObject *                             obj,
-                                            struct _nm_platform_link_get_by_address_data *d)
-{
-    return obj->link.l_address.len == d->len && !memcmp(obj->link.l_address.data, d->data, d->len);
-}
-
-/**
- * nm_platform_link_get_by_address:
- * @self: platform instance
- * @address: a pointer to the binary hardware address
- * @length: the size of @address in bytes
- *
- * Returns: the first #NMPlatformLink object with a matching
- * address.
- **/
-const NMPlatformLink *
-nm_platform_link_get_by_address(NMPlatform *  self,
-                                NMLinkType    link_type,
-                                gconstpointer address,
-                                size_t        length)
-{
-    const NMPObject *                            obj;
-    struct _nm_platform_link_get_by_address_data d = {
-        .data = address,
-        .len  = length,
-    };
-
-    _CHECK_SELF(self, klass, NULL);
-
-    if (length == 0)
-        return NULL;
-
-    if (length > NM_UTILS_HWADDR_LEN_MAX)
-        g_return_val_if_reached(NULL);
-    if (!address)
-        g_return_val_if_reached(NULL);
-
-    obj = nmp_cache_lookup_link_full(nm_platform_get_cache(self),
-                                     0,
-                                     NULL,
-                                     TRUE,
-                                     link_type,
-                                     (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link,
-                                     &d);
-    return NMP_OBJECT_CAST_LINK(obj);
-}
-
-static int
-_link_add_check_existing(NMPlatform *           self,
-                         const char *           name,
-                         NMLinkType             type,
-                         const NMPlatformLink **out_link)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get_by_ifname(self, name);
-    if (pllink) {
-        gboolean wrong_type;
-
-        wrong_type = type != NM_LINK_TYPE_NONE && pllink->type != type;
-        _LOG2D("link: skip adding link due to existing interface of type %s%s%s",
-               nm_link_type_to_string(pllink->type),
-               wrong_type ? ", expected " : "",
-               wrong_type ? nm_link_type_to_string(type) : "");
-        if (out_link)
-            *out_link = pllink;
-        if (wrong_type)
-            return -NME_PL_WRONG_TYPE;
-        return -NME_PL_EXISTS;
-    }
-    if (out_link)
-        *out_link = NULL;
-    return 0;
-}
-
-/**
- * nm_platform_link_add:
- * @self: platform instance
- * @type: Interface type
- * @name: Interface name
- * @parent: the IFLA_LINK parameter or 0.
- * @address: (allow-none): set the mac address of the link
- * @address_len: the length of the @address
- * @extra_data: depending on @type, additional data.
- * @out_link: on success, the link object
- *
- * Add a software interface.  If the interface already exists and is of type
- * @type, return -NME_PL_EXISTS and returns the link
- * in @out_link.  If the interface already exists and is not of type @type,
- * return -NME_PL_WRONG_TYPE.
- *
- * Any link-changed ADDED signal will be emitted directly, before this
- * function finishes.
- *
- * Returns: the negative nm-error on failure.
- */
-int
-nm_platform_link_add(NMPlatform *           self,
-                     NMLinkType             type,
-                     const char *           name,
-                     int                    parent,
-                     const void *           address,
-                     size_t                 address_len,
-                     guint32                mtu,
-                     gconstpointer          extra_data,
-                     const NMPlatformLink **out_link)
-{
-    int  r;
-    char addr_buf[NM_UTILS_HWADDR_LEN_MAX * 3];
-    char mtu_buf[16];
-    char parent_buf[64];
-    char buf[512];
-
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    g_return_val_if_fail(name, -NME_BUG);
-    g_return_val_if_fail((address != NULL) ^ (address_len == 0), -NME_BUG);
-    g_return_val_if_fail(address_len <= NM_UTILS_HWADDR_LEN_MAX, -NME_BUG);
-    g_return_val_if_fail(parent >= 0, -NME_BUG);
-
-    r = _link_add_check_existing(self, name, type, out_link);
-    if (r < 0)
-        return r;
-
-    _LOG2D("link: adding link: "
-           "%s "    /* type */
-           "\"%s\"" /* name */
-           "%s%s"   /* parent */
-           "%s%s"   /* address */
-           "%s%s"   /* mtu */
-           "%s"     /* extra_data */
-           "",
-           nm_link_type_to_string(type),
-           name,
-           parent > 0 ? ", parent " : "",
-           parent > 0 ? nm_sprintf_buf(parent_buf, "%d", parent) : "",
-           address ? ", address: " : "",
-           address ? _nm_utils_hwaddr_ntoa(address, address_len, FALSE, addr_buf, sizeof(addr_buf))
-                   : "",
-           mtu ? ", mtu: " : "",
-           mtu ? nm_sprintf_buf(mtu_buf, "%u", mtu) : "",
-           ({
-               char *buf_p   = buf;
-               gsize buf_len = sizeof(buf);
-
-               buf[0] = '\0';
-
-               switch (type) {
-               case NM_LINK_TYPE_BRIDGE:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_bridge_to_string((const NMPlatformLnkBridge *) extra_data,
-                                                    buf_p,
-                                                    buf_len);
-                   break;
-               case NM_LINK_TYPE_VLAN:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_vlan_to_string((const NMPlatformLnkVlan *) extra_data,
-                                                  buf_p,
-                                                  buf_len);
-                   break;
-               case NM_LINK_TYPE_VRF:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_vrf_to_string((const NMPlatformLnkVrf *) extra_data,
-                                                 buf_p,
-                                                 buf_len);
-                   break;
-               case NM_LINK_TYPE_VXLAN:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_vxlan_to_string((const NMPlatformLnkVxlan *) extra_data,
-                                                   buf_p,
-                                                   buf_len);
-                   break;
-               case NM_LINK_TYPE_VETH:
-                   nm_sprintf_buf(buf, ", veth-peer \"%s\"", (const char *) extra_data);
-                   break;
-               case NM_LINK_TYPE_GRE:
-               case NM_LINK_TYPE_GRETAP:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_gre_to_string((const NMPlatformLnkGre *) extra_data,
-                                                 buf_p,
-                                                 buf_len);
-                   break;
-               case NM_LINK_TYPE_SIT:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_sit_to_string((const NMPlatformLnkSit *) extra_data,
-                                                 buf_p,
-                                                 buf_len);
-                   break;
-               case NM_LINK_TYPE_IP6TNL:
-               case NM_LINK_TYPE_IP6GRE:
-               case NM_LINK_TYPE_IP6GRETAP:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_ip6tnl_to_string((const NMPlatformLnkIp6Tnl *) extra_data,
-                                                    buf_p,
-                                                    buf_len);
-                   break;
-               case NM_LINK_TYPE_IPIP:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_ipip_to_string((const NMPlatformLnkIpIp *) extra_data,
-                                                  buf_p,
-                                                  buf_len);
-                   break;
-               case NM_LINK_TYPE_MACSEC:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_macsec_to_string((const NMPlatformLnkMacsec *) extra_data,
-                                                    buf_p,
-                                                    buf_len);
-                   break;
-               case NM_LINK_TYPE_MACVLAN:
-               case NM_LINK_TYPE_MACVTAP:
-                   nm_utils_strbuf_append_str(&buf_p, &buf_len, ", ");
-                   nm_platform_lnk_macvlan_to_string((const NMPlatformLnkMacvlan *) extra_data,
-                                                     buf_p,
-                                                     buf_len);
-                   break;
-               default:
-                   nm_assert(!extra_data);
-                   break;
-               }
-
-               buf;
-           }));
-
-    return klass
-        ->link_add(self, type, name, parent, address, address_len, mtu, extra_data, out_link);
-}
-
-/**
- * nm_platform_link_delete:
- * @self: platform instance
- * @ifindex: Interface index
- */
-gboolean
-nm_platform_link_delete(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    _LOG3D("link: deleting");
-    return klass->link_delete(self, ifindex);
-}
-
-/**
- * nm_platform_link_set_netns:
- * @self: platform instance
- * @ifindex: Interface index
- * @netns_fd: the file descriptor for the new netns.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-nm_platform_link_set_netns(NMPlatform *self, int ifindex, int netns_fd)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(netns_fd > 0, FALSE);
-
-    _LOG3D("link: move link to network namespace with fd %d", netns_fd);
-    return klass->link_set_netns(self, ifindex, netns_fd);
-}
-
-/**
- * nm_platform_link_get_index:
- * @self: platform instance
- * @name: Interface name
- *
- * Returns: The interface index corresponding to the given interface name
- * or 0. Interface name is owned by #NMPlatform, don't free it.
- */
-int
-nm_platform_link_get_ifindex(NMPlatform *self, const char *name)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get_by_ifname(self, name);
-    return pllink ? pllink->ifindex : 0;
-}
-
-const char *
-nm_platform_if_indextoname(NMPlatform *self, int ifindex, char out_ifname[static 16 /* IFNAMSIZ */])
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    return nmp_utils_if_indextoname(ifindex, out_ifname);
-}
-
-int
-nm_platform_if_nametoindex(NMPlatform *self, const char *ifname)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    return nmp_utils_if_nametoindex(ifname);
-}
-
-/**
- * nm_platform_link_get_name:
- * @self: platform instance
- * @name: Interface name
- *
- * Returns: The interface name corresponding to the given interface index
- * or %NULL.
- */
-const char *
-nm_platform_link_get_name(NMPlatform *self, int ifindex)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get(self, ifindex);
-    return pllink ? pllink->name : NULL;
-}
-
-/**
- * nm_platform_link_get_type:
- * @self: platform instance
- * @ifindex: Interface index.
- *
- * Returns: Link type constant as defined in nm-platform.h. On error,
- * NM_LINK_TYPE_NONE is returned.
- */
-NMLinkType
-nm_platform_link_get_type(NMPlatform *self, int ifindex)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get(self, ifindex);
-    return pllink ? pllink->type : NM_LINK_TYPE_NONE;
-}
-
-/**
- * nm_platform_link_get_type_name:
- * @self: platform instance
- * @ifindex: Interface index.
- *
- * Returns: A string describing the type of link. In some cases this
- * may be more specific than nm_platform_link_get_type(), but in
- * other cases it may not. On error, %NULL is returned.
- */
-const char *
-nm_platform_link_get_type_name(NMPlatform *self, int ifindex)
-{
-    const NMPObject *obj;
-
-    obj = nm_platform_link_get_obj(self, ifindex, TRUE);
-    if (!obj)
-        return NULL;
-
-    if (obj->link.type != NM_LINK_TYPE_UNKNOWN) {
-        /* We could detect the @link_type. In this case the function returns
-         * our internal module names, which differs from rtnl_link_get_type():
-         *   - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib")
-         *   - NM_LINK_TYPE_TAP (gives "tap", instead of "tun").
-         * Note that this functions is only used by NMDeviceGeneric to
-         * set type_description. */
-        return nm_link_type_to_string(obj->link.type);
-    }
-    /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */
-    return obj->link.kind ?: "unknown";
-}
-
-static gboolean
-link_get_udev_property(NMPlatform *self, int ifindex, const char *name, const char **out_value)
-{
-    struct udev_device *udevice = NULL;
-    const char *        uproperty;
-
-    udevice = nm_platform_link_get_udev_device(self, ifindex);
-    if (!udevice)
-        return FALSE;
-
-    uproperty = udev_device_get_property_value(udevice, name);
-    if (!uproperty)
-        return FALSE;
-
-    NM_SET_OUT(out_value, uproperty);
-    return TRUE;
-}
-
-/**
- * nm_platform_link_get_unmanaged:
- * @self: platform instance
- * @ifindex: interface index
- * @unmanaged: management status (in case %TRUE is returned)
- *
- * Returns: %TRUE if platform overrides NM default-unmanaged status,
- * %FALSE otherwise (with @unmanaged unmodified).
- */
-gboolean
-nm_platform_link_get_unmanaged(NMPlatform *self, int ifindex, gboolean *unmanaged)
-{
-    const char *value;
-
-    if (link_get_udev_property(self, ifindex, "NM_UNMANAGED", &value)) {
-        NM_SET_OUT(unmanaged, nm_udev_utils_property_as_boolean(value));
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-/**
- * nm_platform_link_is_software:
- * @self: platform instance
- * @ifindex: Interface index.
- *
- * Returns: %TRUE if ifindex belongs to a software interface, not backed by
- * a physical device.
- */
-gboolean
-nm_platform_link_is_software(NMPlatform *self, int ifindex)
-{
-    return nm_link_type_is_software(nm_platform_link_get_type(self, ifindex));
-}
-
-/**
- * nm_platform_link_supports_slaves:
- * @self: platform instance
- * @ifindex: Interface index.
- *
- * Returns: %TRUE if ifindex belongs to an interface capable of enslaving
- * other interfaces.
- */
-gboolean
-nm_platform_link_supports_slaves(NMPlatform *self, int ifindex)
-{
-    return nm_link_type_supports_slaves(nm_platform_link_get_type(self, ifindex));
-}
-
-/**
- * nm_platform_link_refresh:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Reload the cache for ifindex synchronously.
- */
-gboolean
-nm_platform_link_refresh(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    if (klass->link_refresh)
-        return klass->link_refresh(self, ifindex);
-
-    return TRUE;
-}
-
-int
-nm_platform_link_get_ifi_flags(NMPlatform *self, int ifindex, guint requested_flags)
-{
-    const NMPlatformLink *pllink;
-
-    /* include invisible links (only in netlink, not udev). */
-    pllink = NMP_OBJECT_CAST_LINK(nm_platform_link_get_obj(self, ifindex, FALSE));
-    if (!pllink)
-        return -ENODEV;
-
-    /* Errors are signaled as negative values. That means, you cannot request
-     * the most significant bit (2^31) with this API. Assert against that. */
-    nm_assert((int) requested_flags >= 0);
-    nm_assert(requested_flags < (guint) G_MAXINT);
-
-    return (int) (pllink->n_ifi_flags & requested_flags);
-}
-
-/**
- * nm_platform_link_is_up:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Check if the interface is up.
- */
-gboolean
-nm_platform_link_is_up(NMPlatform *self, int ifindex)
-{
-    return nm_platform_link_get_ifi_flags(self, ifindex, IFF_UP) == IFF_UP;
-}
-
-/**
- * nm_platform_link_is_connected:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Check if the interface is connected.
- */
-gboolean
-nm_platform_link_is_connected(NMPlatform *self, int ifindex)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get(self, ifindex);
-    return pllink ? pllink->connected : FALSE;
-}
-
-/**
- * nm_platform_link_uses_arp:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Check if the interface is configured to use ARP.
- */
-gboolean
-nm_platform_link_uses_arp(NMPlatform *self, int ifindex)
-{
-    int f;
-
-    f = nm_platform_link_get_ifi_flags(self, ifindex, IFF_NOARP);
-
-    if (f < 0)
-        return FALSE;
-    if (f == IFF_NOARP)
-        return FALSE;
-    return TRUE;
-}
-
-/**
- * nm_platform_link_set_ipv6_token:
- * @self: platform instance
- * @ifindex: Interface index
- * @iid: Tokenized interface identifier
- *
- * Sets then IPv6 tokenized interface identifier.
- *
- * Returns: %TRUE a tokenized identifier was available
- */
-gboolean
-nm_platform_link_set_ipv6_token(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    if (klass->link_set_token)
-        return klass->link_set_token(self, ifindex, iid);
-    return FALSE;
-}
-
-const char *
-nm_platform_link_get_udi(NMPlatform *self, int ifindex)
-{
-    struct udev_device *device;
-
-    device = nm_platform_link_get_udev_device(self, ifindex);
-    return device ? udev_device_get_syspath(device) : NULL;
-}
-
-const char *
-nm_platform_link_get_path(NMPlatform *self, int ifindex)
-{
-    const char *value = NULL;
-
-    link_get_udev_property(self, ifindex, "ID_PATH", &value);
-
-    return value;
-}
-
-struct udev_device *
-nm_platform_link_get_udev_device(NMPlatform *self, int ifindex)
-{
-    const NMPObject *obj_cache;
-
-    obj_cache = nm_platform_link_get_obj(self, ifindex, FALSE);
-    return obj_cache ? obj_cache->_link.udev.device : NULL;
-}
-
-/**
- * nm_platform_link_get_user_ip6vll_enabled:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Check whether NM handles IPv6LL address creation for the link.  If the
- * platform or OS doesn't support changing the IPv6LL address mode, this call
- * will fail and return %FALSE.
- *
- * Returns: %TRUE if NM handles the IPv6LL address for @ifindex
- */
-gboolean
-nm_platform_link_get_user_ipv6ll_enabled(NMPlatform *self, int ifindex)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get(self, ifindex);
-    if (pllink && pllink->inet6_addr_gen_mode_inv)
-        return _nm_platform_uint8_inv(pllink->inet6_addr_gen_mode_inv) == NM_IN6_ADDR_GEN_MODE_NONE;
-    return FALSE;
-}
-
-/**
- * nm_platform_link_set_user_ip6vll_enabled:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Set whether NM handles IPv6LL address creation for the link.  If the
- * platform or OS doesn't support changing the IPv6LL address mode, this call
- * will fail and return %FALSE.
- *
- * Returns: the negative nm-error on failure.
- */
-int
-nm_platform_link_set_user_ipv6ll_enabled(NMPlatform *self, int ifindex, gboolean enabled)
-{
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    g_return_val_if_fail(ifindex > 0, -NME_BUG);
-
-    return klass->link_set_user_ipv6ll_enabled(self, ifindex, enabled);
-}
-
-/**
- * nm_platform_link_set_address:
- * @self: platform instance
- * @ifindex: Interface index
- * @address: The new MAC address
- *
- * Set interface MAC address.
- */
-int
-nm_platform_link_set_address(NMPlatform *self, int ifindex, gconstpointer address, size_t length)
-{
-    gs_free char *mac = NULL;
-
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    g_return_val_if_fail(ifindex > 0, -NME_BUG);
-    g_return_val_if_fail(address, -NME_BUG);
-    g_return_val_if_fail(length > 0, -NME_BUG);
-
-    _LOG3D("link: setting hardware address to %s", (mac = nm_utils_hwaddr_ntoa(address, length)));
-    return klass->link_set_address(self, ifindex, address, length);
-}
-
-/**
- * nm_platform_link_get_address:
- * @self: platform instance
- * @ifindex: Interface index
- * @length: Pointer to a variable to store address length
- *
- * Returns: the interface hardware address as an array of bytes of
- * length @length.
- */
-gconstpointer
-nm_platform_link_get_address(NMPlatform *self, int ifindex, size_t *length)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get(self, ifindex);
-    return nmp_link_address_get(pllink ? &pllink->l_address : NULL, length);
-}
-
-/**
- * nm_platform_link_get_permanent_address:
- * @self: platform instance
- * @ifindex: Interface index
- * @buf: buffer of at least %NM_UTILS_HWADDR_LEN_MAX bytes, on success
- * the permanent hardware address
- * @length: Pointer to a variable to store address length
- *
- * Returns: %TRUE on success, %FALSE on failure to read the permanent hardware
- * address.
- */
-gboolean
-nm_platform_link_get_permanent_address(NMPlatform *self, int ifindex, guint8 *buf, size_t *length)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    if (length)
-        *length = 0;
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(buf, FALSE);
-    g_return_val_if_fail(length, FALSE);
-
-    if (klass->link_get_permanent_address)
-        return klass->link_get_permanent_address(self, ifindex, buf, length);
-    return FALSE;
-}
-
-gboolean
-nm_platform_link_supports_carrier_detect(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    return klass->link_supports_carrier_detect(self, ifindex);
-}
-
-gboolean
-nm_platform_link_supports_vlans(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    return klass->link_supports_vlans(self, ifindex);
-}
-
-gboolean
-nm_platform_link_supports_sriov(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    return klass->link_supports_sriov(self, ifindex);
-}
-
-/**
- * nm_platform_link_set_sriov_params:
- * @self: platform instance
- * @ifindex: the index of the interface to change
- * @num_vfs: the number of VFs to create
- * @autoprobe: the new autoprobe-drivers value (pass
- *     %NM_OPTION_BOOL_DEFAULT to keep current value)
- * @callback: called when the operation finishes
- * @callback_data: data passed to @callback
- * @cancellable: cancellable to abort the operation
- *
- * Sets SR-IOV parameters asynchronously without
- * blocking the main thread. The callback function is
- * always invoked, and asynchronously.
- */
-void
-nm_platform_link_set_sriov_params_async(NMPlatform *            self,
-                                        int                     ifindex,
-                                        guint                   num_vfs,
-                                        NMOptionBool            autoprobe,
-                                        NMPlatformAsyncCallback callback,
-                                        gpointer                callback_data,
-                                        GCancellable *          cancellable)
-{
-    _CHECK_SELF_VOID(self, klass);
-
-    g_return_if_fail(ifindex > 0);
-
-    _LOG3D("link: setting %u total VFs and autoprobe %d", num_vfs, (int) autoprobe);
-    klass->link_set_sriov_params_async(self,
-                                       ifindex,
-                                       num_vfs,
-                                       autoprobe,
-                                       callback,
-                                       callback_data,
-                                       cancellable);
-}
-
-gboolean
-nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs)
-{
-    guint i;
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    _LOG3D("link: setting VFs");
-    for (i = 0; vfs[i]; i++) {
-        const NMPlatformVF *vf = vfs[i];
-
-        _LOG3D("link:   VF %s", nm_platform_vf_to_string(vf, NULL, 0));
-    }
-
-    return klass->link_set_sriov_vfs(self, ifindex, vfs);
-}
-
-gboolean
-nm_platform_link_set_bridge_vlans(NMPlatform *                       self,
-                                  int                                ifindex,
-                                  gboolean                           on_master,
-                                  const NMPlatformBridgeVlan *const *vlans)
-{
-    guint i;
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    _LOG3D("link: %s bridge VLANs on %s",
-           vlans ? "setting" : "clearing",
-           on_master ? "master" : "self");
-    if (vlans) {
-        for (i = 0; vlans[i]; i++) {
-            const NMPlatformBridgeVlan *vlan = vlans[i];
-
-            _LOG3D("link:   bridge VLAN %s", nm_platform_bridge_vlan_to_string(vlan, NULL, 0));
-        }
-    }
-
-    return klass->link_set_bridge_vlans(self, ifindex, on_master, vlans);
-}
-
-/**
- * nm_platform_link_set_up:
- * @self: platform instance
- * @ifindex: Interface index
- * @out_no_firmware: (allow-none): if the failure reason is due to missing firmware.
- *
- * Bring the interface up.
- */
-gboolean
-nm_platform_link_set_up(NMPlatform *self, int ifindex, gboolean *out_no_firmware)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    _LOG3D("link: setting up");
-    return klass->link_set_up(self, ifindex, out_no_firmware);
-}
-
-/**
- * nm_platform_link_set_down:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Take the interface down.
- */
-gboolean
-nm_platform_link_set_down(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    _LOG3D("link: setting down");
-    return klass->link_set_down(self, ifindex);
-}
-
-/**
- * nm_platform_link_set_arp:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Enable ARP on the interface.
- */
-gboolean
-nm_platform_link_set_arp(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    _LOG3D("link: setting arp");
-    return klass->link_set_arp(self, ifindex);
-}
-
-/**
- * nm_platform_link_set_noarp:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Disable ARP on the interface.
- */
-gboolean
-nm_platform_link_set_noarp(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    _LOG3D("link: setting noarp");
-    return klass->link_set_noarp(self, ifindex);
-}
-
-/**
- * nm_platform_link_set_mtu:
- * @self: platform instance
- * @ifindex: Interface index
- * @mtu: The new MTU value
- *
- * Set interface MTU.
- */
-int
-nm_platform_link_set_mtu(NMPlatform *self, int ifindex, guint32 mtu)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-    g_return_val_if_fail(mtu > 0, FALSE);
-
-    _LOG3D("link: setting mtu %" G_GUINT32_FORMAT, mtu);
-    return klass->link_set_mtu(self, ifindex, mtu);
-}
-
-/**
- * nm_platform_link_get_mtu:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Returns: MTU value for the interface or 0 on error.
- */
-guint32
-nm_platform_link_get_mtu(NMPlatform *self, int ifindex)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get(self, ifindex);
-    return pllink ? pllink->mtu : 0;
-}
-
-/**
- * nm_platform_link_set_name:
- * @self: platform instance
- * @ifindex: Interface index
- * @name: The new interface name
- *
- * Set interface name.
- */
-gboolean
-nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-    g_return_val_if_fail(name, FALSE);
-
-    _LOG3D("link: setting name %s", name);
-
-    if (strlen(name) + 1 > IFNAMSIZ)
-        return FALSE;
-
-    return klass->link_set_name(self, ifindex, name);
-}
-
-/**
- * nm_platform_link_get_physical_port_id:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * The physical port ID, if present, indicates some unique identifier of
- * the parent interface (eg, the physical port of which this link is a child).
- * Two links that report the same physical port ID can be assumed to be
- * children of the same physical port and may share resources that limit
- * their abilities.
- *
- * Returns: physical port ID for the interface, or %NULL on error
- * or if the interface has no physical port ID.
- */
-char *
-nm_platform_link_get_physical_port_id(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, NULL);
-
-    g_return_val_if_fail(ifindex >= 0, NULL);
-
-    if (klass->link_get_physical_port_id)
-        return klass->link_get_physical_port_id(self, ifindex);
-    return NULL;
-}
-
-/**
- * nm_platform_link_get_dev_id:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * In contrast to the physical device ID (which indicates which parent a
- * child has) the device ID differentiates sibling devices that may share
- * the same MAC address.
- *
- * Returns: device ID for the interface, or 0 on error or if the
- * interface has no device ID.
- */
-guint
-nm_platform_link_get_dev_id(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, 0);
-
-    g_return_val_if_fail(ifindex >= 0, 0);
-
-    if (klass->link_get_dev_id)
-        return klass->link_get_dev_id(self, ifindex);
-    return 0;
-}
-
-/**
- * nm_platform_link_get_wake_onlan:
- * @self: platform instance
- * @ifindex: Interface index
- *
- * Returns: the "Wake-on-LAN" status for @ifindex.
- */
-gboolean
-nm_platform_link_get_wake_on_lan(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    if (klass->link_get_wake_on_lan)
-        return klass->link_get_wake_on_lan(self, ifindex);
-    return FALSE;
-}
-
-/**
- * nm_platform_link_get_driver_info:
- * @self: platform instance
- * @ifindex: Interface index
- * @out_driver_name: (transfer full): on success, the driver name if available
- * @out_driver_version: (transfer full): on success, the driver version if available
- * @out_fw_version: (transfer full): on success, the firmware version if available
- *
- * Returns: %TRUE on success (though @out_driver_name, @out_driver_version and
- * @out_fw_version can be %NULL if no information was available), %FALSE on
- * failure.
- */
-gboolean
-nm_platform_link_get_driver_info(NMPlatform *self,
-                                 int         ifindex,
-                                 char **     out_driver_name,
-                                 char **     out_driver_version,
-                                 char **     out_fw_version)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex >= 0, FALSE);
-
-    return klass->link_get_driver_info(self,
-                                       ifindex,
-                                       out_driver_name,
-                                       out_driver_version,
-                                       out_fw_version);
-}
-
-/**
- * nm_platform_link_enslave:
- * @self: platform instance
- * @master: Interface index of the master
- * @ifindex: Interface index of the slave
- *
- * Enslave @ifindex to @master.
- */
-gboolean
-nm_platform_link_enslave(NMPlatform *self, int master, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(master > 0, FALSE);
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    _LOG3D("link: enslaving to master '%s'", nm_platform_link_get_name(self, master));
-    return klass->link_enslave(self, master, ifindex);
-}
-
-/**
- * nm_platform_link_release:
- * @self: platform instance
- * @master: Interface index of the master
- * @ifindex: Interface index of the slave
- *
- * Release @slave from @master.
- */
-gboolean
-nm_platform_link_release(NMPlatform *self, int master, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(master > 0, FALSE);
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    if (nm_platform_link_get_master(self, ifindex) != master)
-        return FALSE;
-
-    _LOG3D("link: releasing from master '%s'", nm_platform_link_get_name(self, master));
-    return klass->link_release(self, master, ifindex);
-}
-
-/**
- * nm_platform_link_get_master:
- * @self: platform instance
- * @slave: Interface index of the slave.
- *
- * Returns: Interface index of the slave's master.
- */
-int
-nm_platform_link_get_master(NMPlatform *self, int slave)
-{
-    const NMPlatformLink *pllink;
-
-    pllink = nm_platform_link_get(self, slave);
-    return pllink ? pllink->master : 0;
-}
-
-/*****************************************************************************/
-
-gboolean
-nm_platform_link_can_assume(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    if (klass->link_can_assume)
-        return klass->link_can_assume(self, ifindex);
-    g_return_val_if_reached(FALSE);
-}
-
-/*****************************************************************************/
-
-/**
- * nm_platform_link_get_lnk:
- * @self: the platform instance
- * @ifindex: the link ifindex to lookup
- * @link_type: filter by link-type.
- * @out_link: (allow-none): returns the platform link instance
- *
- * If the function returns %NULL, that could mean that no such ifindex
- * exists, of that the link has no lnk data. You can find that out
- * by checking @out_link. @out_link will always be set if a link
- * with @ifindex exists.
- *
- * If @link_type is %NM_LINK_TYPE_NONE, the function returns the lnk
- * object if it is present. If you set link-type, you can be sure
- * that only a link type of the matching type is returned (or %NULL).
- *
- * Returns: the internal link lnk object. The returned object
- * is owned by the platform cache and must not be modified. Note
- * however, that the object is guaranteed to be immutable, so
- * you can safely take a reference and keep it for yourself
- * (but don't modify it).
- */
-const NMPObject *
-nm_platform_link_get_lnk(NMPlatform *           self,
-                         int                    ifindex,
-                         NMLinkType             link_type,
-                         const NMPlatformLink **out_link)
-{
-    const NMPObject *obj;
-
-    obj = nm_platform_link_get_obj(self, ifindex, TRUE);
-    if (!obj) {
-        NM_SET_OUT(out_link, NULL);
-        return NULL;
-    }
-
-    NM_SET_OUT(out_link, &obj->link);
-
-    if (!obj->_link.netlink.lnk)
-        return NULL;
-    if (link_type != NM_LINK_TYPE_NONE
-        && (link_type != obj->link.type
-            || link_type != NMP_OBJECT_GET_CLASS(obj->_link.netlink.lnk)->lnk_link_type))
-        return NULL;
-
-    return obj->_link.netlink.lnk;
-}
-
-static gconstpointer
-_link_get_lnk(NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link)
-{
-    const NMPObject *lnk;
-
-    lnk = nm_platform_link_get_lnk(self, ifindex, link_type, out_link);
-    return lnk ? &lnk->object : NULL;
-}
-
-const NMPlatformLnkBridge *
-nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_BRIDGE, out_link);
-}
-
-const NMPlatformLnkGre *
-nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_GRE, out_link);
-}
-
-const NMPlatformLnkGre *
-nm_platform_link_get_lnk_gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_GRETAP, out_link);
-}
-
-const NMPlatformLnkInfiniband *
-nm_platform_link_get_lnk_infiniband(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_INFINIBAND, out_link);
-}
-
-const NMPlatformLnkIp6Tnl *
-nm_platform_link_get_lnk_ip6tnl(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IP6TNL, out_link);
-}
-
-const NMPlatformLnkIp6Tnl *
-nm_platform_link_get_lnk_ip6gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IP6GRE, out_link);
-}
-
-const NMPlatformLnkIp6Tnl *
-nm_platform_link_get_lnk_ip6gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IP6GRETAP, out_link);
-}
-
-const NMPlatformLnkIpIp *
-nm_platform_link_get_lnk_ipip(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IPIP, out_link);
-}
-
-const NMPlatformLnkMacsec *
-nm_platform_link_get_lnk_macsec(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_MACSEC, out_link);
-}
-
-const NMPlatformLnkMacvlan *
-nm_platform_link_get_lnk_macvlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_MACVLAN, out_link);
-}
-
-const NMPlatformLnkMacvlan *
-nm_platform_link_get_lnk_macvtap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_MACVTAP, out_link);
-}
-
-const NMPlatformLnkSit *
-nm_platform_link_get_lnk_sit(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_SIT, out_link);
-}
-
-const NMPlatformLnkTun *
-nm_platform_link_get_lnk_tun(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_TUN, out_link);
-}
-
-const NMPlatformLnkVlan *
-nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VLAN, out_link);
-}
-
-const NMPlatformLnkVrf *
-nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VRF, out_link);
-}
-
-const NMPlatformLnkVxlan *
-nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VXLAN, out_link);
-}
-
-const NMPlatformLnkWireGuard *
-nm_platform_link_get_lnk_wireguard(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-    return _link_get_lnk(self, ifindex, NM_LINK_TYPE_WIREGUARD, out_link);
-}
-
-/*****************************************************************************/
-
-static NM_UTILS_FLAGS2STR_DEFINE(
-    _wireguard_change_flags_to_string,
-    NMPlatformWireGuardChangeFlags,
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE, "none"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS, "replace-peers"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY, "has-private-key"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT, "has-listen-port"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK, "has-fwmark"), );
-
-static NM_UTILS_FLAGS2STR_DEFINE(
-    _wireguard_change_peer_flags_to_string,
-    NMPlatformWireGuardChangePeerFlags,
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE, "none"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME, "remove"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY, "psk"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL, "ka"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT, "ep"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS, "aips"),
-    NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS, "remove-aips"), );
-
-int
-nm_platform_link_wireguard_change(NMPlatform *                              self,
-                                  int                                       ifindex,
-                                  const NMPlatformLnkWireGuard *            lnk_wireguard,
-                                  const NMPWireGuardPeer *                  peers,
-                                  const NMPlatformWireGuardChangePeerFlags *peer_flags,
-                                  guint                                     peers_len,
-                                  NMPlatformWireGuardChangeFlags            change_flags)
-{
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    nm_assert(klass->link_wireguard_change);
-
-    if (_LOGD_ENABLED()) {
-        char buf_lnk[256];
-        char buf_peers[512];
-        char buf_change_flags[100];
-
-        buf_peers[0] = '\0';
-        if (peers_len > 0) {
-            char *b   = buf_peers;
-            gsize len = sizeof(buf_peers);
-            guint i;
-
-            nm_utils_strbuf_append_str(&b, &len, " { ");
-            for (i = 0; i < peers_len; i++) {
-                nm_utils_strbuf_append_str(&b, &len, " { ");
-                nm_platform_wireguard_peer_to_string(&peers[i], b, len);
-                nm_utils_strbuf_seek_end(&b, &len);
-                if (peer_flags) {
-                    nm_utils_strbuf_append(
-                        &b,
-                        &len,
-                        " (%s)",
-                        _wireguard_change_peer_flags_to_string(peer_flags[i],
-                                                               buf_change_flags,
-                                                               sizeof(buf_change_flags)));
-                }
-                nm_utils_strbuf_append_str(&b, &len, " } ");
-            }
-            nm_utils_strbuf_append_str(&b, &len, "}");
-        }
-
-        _LOG3D("link: change wireguard ifindex %d, %s, (%s), %u peers%s",
-               ifindex,
-               nm_platform_lnk_wireguard_to_string(lnk_wireguard, buf_lnk, sizeof(buf_lnk)),
-               _wireguard_change_flags_to_string(change_flags,
-                                                 buf_change_flags,
-                                                 sizeof(buf_change_flags)),
-               peers_len,
-               buf_peers);
-    }
-
-    return klass->link_wireguard_change(self,
-                                        ifindex,
-                                        lnk_wireguard,
-                                        peers,
-                                        peer_flags,
-                                        peers_len,
-                                        change_flags);
-}
-
-/*****************************************************************************/
-
-/**
- * nm_platform_link_tun_add:
- * @self: platform instance
- * @name: new interface name
- * @tap: whether the interface is a TAP
- * @owner: interface owner or -1
- * @group: interface group or -1
- * @pi: whether to clear the IFF_NO_PI flag
- * @vnet_hdr: whether to set the IFF_VNET_HDR flag
- * @multi_queue: whether to set the IFF_MULTI_QUEUE flag
- * @out_link: on success, the link object
- * @out_fd: (allow-none): if give, return the file descriptor for the
- *   created device. Note that when creating a non-persistent device,
- *   this argument is mandatory, otherwise it makes no sense
- *   to create such an interface.
- *   The caller is responsible for closing this file descriptor.
- *
- * Create a TUN or TAP interface.
- */
-int
-nm_platform_link_tun_add(NMPlatform *            self,
-                         const char *            name,
-                         const NMPlatformLnkTun *props,
-                         const NMPlatformLink ** out_link,
-                         int *                   out_fd)
-{
-    char b[255];
-    int  r;
-
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    g_return_val_if_fail(name, -NME_BUG);
-    g_return_val_if_fail(props, -NME_BUG);
-    g_return_val_if_fail(NM_IN_SET(props->type, IFF_TUN, IFF_TAP), -NME_BUG);
-
-    /* creating a non-persistent device requires that the caller handles
-     * the file descriptor. */
-    g_return_val_if_fail(props->persist || out_fd, -NME_BUG);
-
-    NM_SET_OUT(out_fd, -1);
-
-    r = _link_add_check_existing(self, name, NM_LINK_TYPE_TUN, out_link);
-    if (r < 0)
-        return r;
-
-    _LOG2D("link: adding link %s", nm_platform_lnk_tun_to_string(props, b, sizeof(b)));
-
-    if (!klass->link_tun_add(self, name, props, out_link, out_fd))
-        return -NME_UNSPEC;
-    return 0;
-}
-
-gboolean
-nm_platform_link_6lowpan_get_properties(NMPlatform *self, int ifindex, int *out_parent)
-{
-    const NMPlatformLink *plink;
-
-    plink = nm_platform_link_get(self, ifindex);
-    if (!plink)
-        return FALSE;
-
-    if (plink->type != NM_LINK_TYPE_6LOWPAN)
-        return FALSE;
-
-    if (plink->parent != 0) {
-        NM_SET_OUT(out_parent, plink->parent);
-        return TRUE;
-    }
-
-    /* As of 4.16 kernel does not expose the peer_ifindex as IFA_LINK.
-     * Find the WPAN device with the same MAC address. */
-    if (out_parent) {
-        const NMPlatformLink *parent_plink;
-
-        parent_plink = nm_platform_link_get_by_address(self,
-                                                       NM_LINK_TYPE_WPAN,
-                                                       plink->l_address.data,
-                                                       plink->l_address.len);
-        NM_SET_OUT(out_parent, parent_plink ? parent_plink->ifindex : -1);
-    }
-
-    return TRUE;
-}
-
-/*****************************************************************************/
-
-static gboolean
-link_set_option(NMPlatform *self,
-                int         ifindex,
-                const char *category,
-                const char *option,
-                const char *value)
-{
-    nm_auto_close int dirfd = -1;
-    char              ifname_verified[IFNAMSIZ];
-    const char *      path;
-
-    if (!category || !option)
-        return FALSE;
-
-    dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname_verified);
-    if (dirfd < 0)
-        return FALSE;
-
-    path =
-        nm_sprintf_buf_unsafe_a(strlen(category) + strlen(option) + 2, "%s/%s", category, option);
-    return nm_platform_sysctl_set(self,
-                                  NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname_verified, path),
-                                  value);
-}
-
-static char *
-link_get_option(NMPlatform *self, int ifindex, const char *category, const char *option)
-{
-    nm_auto_close int dirfd = -1;
-    char              ifname_verified[IFNAMSIZ];
-    const char *      path;
-
-    if (!category || !option)
-        return NULL;
-
-    dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname_verified);
-    if (dirfd < 0)
-        return NULL;
-
-    path =
-        nm_sprintf_buf_unsafe_a(strlen(category) + strlen(option) + 2, "%s/%s", category, option);
-    return nm_platform_sysctl_get(self,
-                                  NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname_verified, path));
-}
-
-static const char *
-master_category(NMPlatform *self, int master)
-{
-    switch (nm_platform_link_get_type(self, master)) {
-    case NM_LINK_TYPE_BRIDGE:
-        return "bridge";
-    case NM_LINK_TYPE_BOND:
-        return "bonding";
-    default:
-        return NULL;
-    }
-}
-
-static const char *
-slave_category(NMPlatform *self, int slave)
-{
-    int master = nm_platform_link_get_master(self, slave);
-
-    if (master <= 0)
-        return NULL;
-
-    switch (nm_platform_link_get_type(self, master)) {
-    case NM_LINK_TYPE_BRIDGE:
-        return "brport";
-    default:
-        return NULL;
-    }
-}
-
-gboolean
-nm_platform_sysctl_master_set_option(NMPlatform *self,
-                                     int         ifindex,
-                                     const char *option,
-                                     const char *value)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(option, FALSE);
-    g_return_val_if_fail(value, FALSE);
-
-    return link_set_option(self, ifindex, master_category(self, ifindex), option, value);
-}
-
-char *
-nm_platform_sysctl_master_get_option(NMPlatform *self, int ifindex, const char *option)
-{
-    _CHECK_SELF(self, klass, NULL);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(option, FALSE);
-
-    return link_get_option(self, ifindex, master_category(self, ifindex), option);
-}
-
-gboolean
-nm_platform_sysctl_slave_set_option(NMPlatform *self,
-                                    int         ifindex,
-                                    const char *option,
-                                    const char *value)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(option, FALSE);
-    g_return_val_if_fail(value, FALSE);
-
-    return link_set_option(self, ifindex, slave_category(self, ifindex), option, value);
-}
-
-char *
-nm_platform_sysctl_slave_get_option(NMPlatform *self, int ifindex, const char *option)
-{
-    _CHECK_SELF(self, klass, NULL);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(option, FALSE);
-
-    return link_get_option(self, ifindex, slave_category(self, ifindex), option);
-}
-
-/*****************************************************************************/
-
-gboolean
-nm_platform_link_vlan_change(NMPlatform *            self,
-                             int                     ifindex,
-                             NMVlanFlags             flags_mask,
-                             NMVlanFlags             flags_set,
-                             gboolean                ingress_reset_all,
-                             const NMVlanQosMapping *ingress_map,
-                             gsize                   n_ingress_map,
-                             gboolean                egress_reset_all,
-                             const NMVlanQosMapping *egress_map,
-                             gsize                   n_egress_map)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    nm_assert(klass->link_vlan_change);
-
-    g_return_val_if_fail(!n_ingress_map || ingress_map, FALSE);
-    g_return_val_if_fail(!n_egress_map || egress_map, FALSE);
-
-    flags_set &= flags_mask;
-
-    if (_LOGD_ENABLED()) {
-        char  buf[512];
-        char *b = buf;
-        gsize len, i;
-
-        b[0] = '\0';
-        len  = sizeof(buf);
-
-        if (flags_mask)
-            nm_utils_strbuf_append(&b,
-                                   &len,
-                                   " flags 0x%x/0x%x",
-                                   (unsigned) flags_set,
-                                   (unsigned) flags_mask);
-
-        if (ingress_reset_all || n_ingress_map) {
-            nm_utils_strbuf_append_str(&b, &len, " ingress-qos-map");
-            nm_platform_vlan_qos_mapping_to_string("", ingress_map, n_ingress_map, b, len);
-            i = strlen(b);
-            b += i;
-            len -= i;
-            if (ingress_reset_all)
-                nm_utils_strbuf_append_str(&b, &len, " (reset-all)");
-        }
-
-        if (egress_reset_all || n_egress_map) {
-            nm_utils_strbuf_append_str(&b, &len, " egress-qos-map");
-            nm_platform_vlan_qos_mapping_to_string("", egress_map, n_egress_map, b, len);
-            i = strlen(b);
-            b += i;
-            len -= i;
-            if (egress_reset_all)
-                nm_utils_strbuf_append_str(&b, &len, " (reset-all)");
-        }
-
-        _LOG3D("link: change vlan %s", buf);
-    }
-    return klass->link_vlan_change(self,
-                                   ifindex,
-                                   flags_mask,
-                                   flags_set,
-                                   ingress_reset_all,
-                                   ingress_map,
-                                   n_ingress_map,
-                                   egress_reset_all,
-                                   egress_map,
-                                   n_egress_map);
-}
-
-gboolean
-nm_platform_link_vlan_set_ingress_map(NMPlatform *self, int ifindex, int from, int to)
-{
-    NMVlanQosMapping map = {
-        .from = from,
-        .to   = to,
-    };
-
-    return nm_platform_link_vlan_change(self, ifindex, 0, 0, FALSE, &map, 1, FALSE, NULL, 0);
-}
-
-gboolean
-nm_platform_link_vlan_set_egress_map(NMPlatform *self, int ifindex, int from, int to)
-{
-    NMVlanQosMapping map = {
-        .from = from,
-        .to   = to,
-    };
-
-    return nm_platform_link_vlan_change(self, ifindex, 0, 0, FALSE, NULL, 0, FALSE, &map, 1);
-}
-
-static int
-_infiniband_add_add_or_delete(NMPlatform *           self,
-                              int                    ifindex,
-                              int                    p_key,
-                              gboolean               add,
-                              const NMPlatformLink **out_link)
-{
-    char                  name[IFNAMSIZ];
-    const NMPlatformLink *parent_link;
-    int                   r;
-
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    g_return_val_if_fail(ifindex >= 0, -NME_BUG);
-    g_return_val_if_fail(p_key >= 0 && p_key <= 0xffff, -NME_BUG);
-
-    /* the special keys 0x0000 and 0x8000 are not allowed. */
-    if (NM_IN_SET(p_key, 0, 0x8000))
-        return -NME_UNSPEC;
-
-    parent_link = nm_platform_link_get(self, ifindex);
-    if (!parent_link)
-        return -NME_PL_NOT_FOUND;
-
-    if (parent_link->type != NM_LINK_TYPE_INFINIBAND)
-        return -NME_PL_WRONG_TYPE;
-
-    nm_utils_new_infiniband_name(name, parent_link->name, p_key);
-
-    if (add) {
-        r = _link_add_check_existing(self, name, NM_LINK_TYPE_INFINIBAND, out_link);
-        if (r < 0)
-            return r;
-
-        _LOG3D("link: adding infiniband partition %s, key %d", name, p_key);
-        if (!klass->infiniband_partition_add(self, ifindex, p_key, out_link))
-            return -NME_UNSPEC;
-    } else {
-        _LOG3D("link: deleting infiniband partition %s, key %d", name, p_key);
-
-        if (!klass->infiniband_partition_delete(self, ifindex, p_key))
-            return -NME_UNSPEC;
-    }
-
-    return 0;
-}
-
-int
-nm_platform_link_infiniband_add(NMPlatform *           self,
-                                int                    parent,
-                                int                    p_key,
-                                const NMPlatformLink **out_link)
-{
-    return _infiniband_add_add_or_delete(self, parent, p_key, TRUE, out_link);
-}
-
-int
-nm_platform_link_infiniband_delete(NMPlatform *self, int parent, int p_key)
-{
-    return _infiniband_add_add_or_delete(self, parent, p_key, FALSE, NULL);
-}
-
-gboolean
-nm_platform_link_infiniband_get_properties(NMPlatform * self,
-                                           int          ifindex,
-                                           int *        out_parent,
-                                           int *        out_p_key,
-                                           const char **out_mode)
-{
-    nm_auto_close int              dirfd = -1;
-    char                           ifname_verified[IFNAMSIZ];
-    const NMPlatformLnkInfiniband *plnk;
-    const NMPlatformLink *         plink;
-    char *                         contents;
-    const char *                   mode;
-    int                            p_key = 0;
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    plnk = nm_platform_link_get_lnk_infiniband(self, ifindex, &plink);
-
-    if (!plink || plink->type != NM_LINK_TYPE_INFINIBAND)
-        return FALSE;
-
-    if (plnk) {
-        NM_SET_OUT(out_parent, plink->parent);
-        NM_SET_OUT(out_p_key, plnk->p_key);
-        NM_SET_OUT(out_mode, plnk->mode);
-        return TRUE;
-    }
-
-    /* Could not get the link information via netlink. To support older kernels,
-     * fallback to reading sysfs. */
-
-    dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname_verified);
-    if (dirfd < 0)
-        return FALSE;
-
-    contents =
-        nm_platform_sysctl_get(self, NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "mode"));
-    if (!contents)
-        return FALSE;
-    if (strstr(contents, "datagram"))
-        mode = "datagram";
-    else if (strstr(contents, "connected"))
-        mode = "connected";
-    else
-        mode = NULL;
-    g_free(contents);
-
-    p_key =
-        nm_platform_sysctl_get_int_checked(self,
-                                           NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "pkey"),
-                                           16,
-                                           0,
-                                           0xFFFF,
-                                           -1);
-    if (p_key < 0)
-        return FALSE;
-
-    NM_SET_OUT(out_parent, plink->parent);
-    NM_SET_OUT(out_p_key, p_key);
-    NM_SET_OUT(out_mode, mode);
-    return TRUE;
-}
-
-gboolean
-nm_platform_link_veth_get_properties(NMPlatform *self, int ifindex, int *out_peer_ifindex)
-{
-    const NMPlatformLink *plink;
-    int                   peer_ifindex;
-
-    plink = nm_platform_link_get(self, ifindex);
-    if (!plink)
-        return FALSE;
-
-    if (plink->type != NM_LINK_TYPE_VETH)
-        return FALSE;
-
-    if (plink->parent != 0) {
-        NM_SET_OUT(out_peer_ifindex, plink->parent);
-        return TRUE;
-    }
-
-    /* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */
-    if (out_peer_ifindex) {
-        nm_auto_pop_netns NMPNetns *netns = NULL;
-
-        if (!nm_platform_netns_push(self, &netns))
-            return FALSE;
-        peer_ifindex = nmp_utils_ethtool_get_peer_ifindex(plink->ifindex);
-        if (peer_ifindex <= 0)
-            return FALSE;
-
-        *out_peer_ifindex = peer_ifindex;
-    }
-    return TRUE;
-}
-
-/**
- * nm_platform_link_tun_get_properties:
- * @self: the #NMPlatform instance
- * @ifindex: the ifindex to look up
- * @out_properties: (out) (allow-none): return the read properties
- *
- * Only recent versions of kernel export tun properties via netlink.
- * So, if that's the case, then we have the NMPlatformLnkTun instance
- * in the platform cache ready to return. Otherwise, this function
- * falls back reading sysctl to obtain the tun properties. That
- * is racy, because querying sysctl means that the object might
- * be already removed from cache (while NM didn't yet process the
- * netlink message).
- *
- * Hence, to lookup the tun properties, you always need to use this
- * function, and use it with care knowing that it might obtain its
- * data by reading sysctl. Note that we don't want to add this workaround
- * to the platform cache itself, because the cache should (mainly)
- * contain data from netlink. To access the sysctl side channel, the
- * user needs to do explicitly.
- *
- * Returns: #TRUE, if the properties could be read. */
-gboolean
-nm_platform_link_tun_get_properties(NMPlatform *self, int ifindex, NMPlatformLnkTun *out_properties)
-{
-    const NMPObject *plobj;
-    const NMPObject *pllnk;
-    char             ifname[IFNAMSIZ];
-    gint64           owner;
-    gint64           group;
-    gint64           flags;
-
-    /* we consider also invisible links (those that are not yet in udev). */
-    plobj = nm_platform_link_get_obj(self, ifindex, FALSE);
-    if (!plobj)
-        return FALSE;
-
-    if (NMP_OBJECT_CAST_LINK(plobj)->type != NM_LINK_TYPE_TUN)
-        return FALSE;
-
-    pllnk = plobj->_link.netlink.lnk;
-    if (pllnk) {
-        nm_assert(NMP_OBJECT_GET_TYPE(pllnk) == NMP_OBJECT_TYPE_LNK_TUN);
-        nm_assert(NMP_OBJECT_GET_CLASS(pllnk)->lnk_link_type == NM_LINK_TYPE_TUN);
-
-        /* recent kernels expose tun properties via netlink and thus we have them
-         * in the platform cache. */
-        NM_SET_OUT(out_properties, pllnk->lnk_tun);
-        return TRUE;
-    }
-
-    /* fallback to reading sysctl. */
-    {
-        nm_auto_close int dirfd = -1;
-
-        dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname);
-        if (dirfd < 0)
-            return FALSE;
-
-        owner = nm_platform_sysctl_get_int_checked(self,
-                                                   NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "owner"),
-                                                   10,
-                                                   -1,
-                                                   G_MAXUINT32,
-                                                   -2);
-        if (owner == -2)
-            return FALSE;
-
-        group = nm_platform_sysctl_get_int_checked(self,
-                                                   NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "group"),
-                                                   10,
-                                                   -1,
-                                                   G_MAXUINT32,
-                                                   -2);
-        if (group == -2)
-            return FALSE;
-
-        flags =
-            nm_platform_sysctl_get_int_checked(self,
-                                               NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "tun_flags"),
-                                               16,
-                                               0,
-                                               G_MAXINT64,
-                                               -1);
-        if (flags == -1)
-            return FALSE;
-    }
-
-    if (out_properties) {
-        memset(out_properties, 0, sizeof(*out_properties));
-        if (owner != -1) {
-            out_properties->owner_valid = TRUE;
-            out_properties->owner       = owner;
-        }
-        if (group != -1) {
-            out_properties->group_valid = TRUE;
-            out_properties->group       = group;
-        }
-        out_properties->type        = (flags & TUN_TYPE_MASK);
-        out_properties->pi          = !(flags & IFF_NO_PI);
-        out_properties->vnet_hdr    = !!(flags & IFF_VNET_HDR);
-        out_properties->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE);
-        out_properties->persist     = !!(flags & IFF_PERSIST);
-    }
-    return TRUE;
-}
-
-gboolean
-nm_platform_wifi_get_capabilities(NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wifi_get_capabilities(self, ifindex, caps);
-}
-
-guint32
-nm_platform_wifi_get_frequency(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, 0);
-
-    g_return_val_if_fail(ifindex > 0, 0);
-
-    return klass->wifi_get_frequency(self, ifindex);
-}
-
-gboolean
-nm_platform_wifi_get_station(NMPlatform * self,
-                             int          ifindex,
-                             NMEtherAddr *out_bssid,
-                             int *        out_quality,
-                             guint32 *    out_rate)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wifi_get_station(self, ifindex, out_bssid, out_quality, out_rate);
-}
-
-NM80211Mode
-nm_platform_wifi_get_mode(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, NM_802_11_MODE_UNKNOWN);
-
-    g_return_val_if_fail(ifindex > 0, NM_802_11_MODE_UNKNOWN);
-
-    return klass->wifi_get_mode(self, ifindex);
-}
-
-void
-nm_platform_wifi_set_mode(NMPlatform *self, int ifindex, NM80211Mode mode)
-{
-    _CHECK_SELF_VOID(self, klass);
-
-    g_return_if_fail(ifindex > 0);
-
-    klass->wifi_set_mode(self, ifindex, mode);
-}
-
-static void
-wifi_set_powersave(NMPlatform *p, int ifindex, guint32 powersave)
-{
-    /* empty */
-}
-
-void
-nm_platform_wifi_set_powersave(NMPlatform *self, int ifindex, guint32 powersave)
-{
-    _CHECK_SELF_VOID(self, klass);
-
-    g_return_if_fail(ifindex > 0);
-
-    klass->wifi_set_powersave(self, ifindex, powersave);
-}
-
-guint32
-nm_platform_wifi_find_frequency(NMPlatform *self, int ifindex, const guint32 *freqs)
-{
-    _CHECK_SELF(self, klass, 0);
-
-    g_return_val_if_fail(ifindex > 0, 0);
-    g_return_val_if_fail(freqs != NULL, 0);
-
-    return klass->wifi_find_frequency(self, ifindex, freqs);
-}
-
-void
-nm_platform_wifi_indicate_addressing_running(NMPlatform *self, int ifindex, gboolean running)
-{
-    _CHECK_SELF_VOID(self, klass);
-
-    g_return_if_fail(ifindex > 0);
-
-    klass->wifi_indicate_addressing_running(self, ifindex, running);
-}
-
-NMSettingWirelessWakeOnWLan
-nm_platform_wifi_get_wake_on_wlan(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wifi_get_wake_on_wlan(self, ifindex);
-}
-
-gboolean
-nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wifi_set_wake_on_wlan(self, ifindex, wowl);
-}
-
-guint32
-nm_platform_mesh_get_channel(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, 0);
-
-    g_return_val_if_fail(ifindex > 0, 0);
-
-    return klass->mesh_get_channel(self, ifindex);
-}
-
-gboolean
-nm_platform_mesh_set_channel(NMPlatform *self, int ifindex, guint32 channel)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->mesh_set_channel(self, ifindex, channel);
-}
-
-gboolean
-nm_platform_mesh_set_ssid(NMPlatform *self, int ifindex, const guint8 *ssid, gsize len)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(ssid != NULL, FALSE);
-
-    return klass->mesh_set_ssid(self, ifindex, ssid, len);
-}
-
-guint16
-nm_platform_wpan_get_pan_id(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wpan_get_pan_id(self, ifindex);
-}
-
-gboolean
-nm_platform_wpan_set_pan_id(NMPlatform *self, int ifindex, guint16 pan_id)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wpan_set_pan_id(self, ifindex, pan_id);
-}
-
-guint16
-nm_platform_wpan_get_short_addr(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wpan_get_short_addr(self, ifindex);
-}
-
-gboolean
-nm_platform_wpan_set_short_addr(NMPlatform *self, int ifindex, guint16 short_addr)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wpan_set_short_addr(self, ifindex, short_addr);
-}
-
-gboolean
-nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 channel)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return klass->wpan_set_channel(self, ifindex, page, channel);
-}
-
-#define TO_STRING_DEV_BUF_SIZE (5 + 15 + 1)
-static const char *
-_to_string_dev(NMPlatform *self, int ifindex, char *buf, size_t size)
-{
-    g_assert(buf && size >= TO_STRING_DEV_BUF_SIZE);
-
-    if (ifindex) {
-        const char *name = ifindex > 0 && self ? nm_platform_link_get_name(self, ifindex) : NULL;
-        char *      buf2;
-
-        strcpy(buf, " dev ");
-        buf2 = buf + 5;
-        size -= 5;
-
-        if (name)
-            g_strlcpy(buf2, name, size);
-        else
-            g_snprintf(buf2, size, "%d", ifindex);
-    } else
-        buf[0] = 0;
-
-    return buf;
-}
-
-#define TO_STRING_IFA_FLAGS_BUF_SIZE 256
-
-static const char *
-_to_string_ifa_flags(guint32 ifa_flags, char *buf, gsize size)
-{
-#define S_FLAGS_PREFIX " flags "
-    nm_assert(buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN(S_FLAGS_PREFIX));
-
-    if (!ifa_flags)
-        buf[0] = '\0';
-    else {
-        nm_platform_addr_flags2str(ifa_flags,
-                                   &buf[NM_STRLEN(S_FLAGS_PREFIX)],
-                                   size - NM_STRLEN(S_FLAGS_PREFIX));
-        if (buf[NM_STRLEN(S_FLAGS_PREFIX)] == '\0')
-            buf[0] = '\0';
-        else
-            memcpy(buf, S_FLAGS_PREFIX, NM_STRLEN(S_FLAGS_PREFIX));
-    }
-    return buf;
-}
-
-/*****************************************************************************/
-
-gboolean
-nm_platform_ethtool_set_wake_on_lan(NMPlatform *             self,
-                                    int                      ifindex,
-                                    _NMSettingWiredWakeOnLan wol,
-                                    const char *             wol_password)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return nmp_utils_ethtool_set_wake_on_lan(ifindex, wol, wol_password);
-}
-
-gboolean
-nm_platform_ethtool_set_link_settings(NMPlatform *             self,
-                                      int                      ifindex,
-                                      gboolean                 autoneg,
-                                      guint32                  speed,
-                                      NMPlatformLinkDuplexType duplex)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return nmp_utils_ethtool_set_link_settings(ifindex, autoneg, speed, duplex);
-}
-
-gboolean
-nm_platform_ethtool_get_link_settings(NMPlatform *              self,
-                                      int                       ifindex,
-                                      gboolean *                out_autoneg,
-                                      guint32 *                 out_speed,
-                                      NMPlatformLinkDuplexType *out_duplex)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return nmp_utils_ethtool_get_link_settings(ifindex, out_autoneg, out_speed, out_duplex);
-}
-
-/*****************************************************************************/
-
-NMEthtoolFeatureStates *
-nm_platform_ethtool_get_link_features(NMPlatform *self, int ifindex)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, NULL);
-
-    g_return_val_if_fail(ifindex > 0, NULL);
-
-    return nmp_utils_ethtool_get_features(ifindex);
-}
-
-gboolean
-nm_platform_ethtool_set_features(
-    NMPlatform *                  self,
-    int                           ifindex,
-    const NMEthtoolFeatureStates *features,
-    const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */,
-    gboolean            do_set /* or reset */)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return nmp_utils_ethtool_set_features(ifindex, features, requested, do_set);
-}
-
-gboolean
-nm_platform_ethtool_get_link_coalesce(NMPlatform *            self,
-                                      int                     ifindex,
-                                      NMEthtoolCoalesceState *coalesce)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(coalesce, FALSE);
-
-    return nmp_utils_ethtool_get_coalesce(ifindex, coalesce);
-}
-
-gboolean
-nm_platform_ethtool_set_coalesce(NMPlatform *                  self,
-                                 int                           ifindex,
-                                 const NMEthtoolCoalesceState *coalesce)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return nmp_utils_ethtool_set_coalesce(ifindex, coalesce);
-}
-
-gboolean
-nm_platform_ethtool_get_link_ring(NMPlatform *self, int ifindex, NMEthtoolRingState *ring)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(ring, FALSE);
-
-    return nmp_utils_ethtool_get_ring(ifindex, ring);
-}
-
-gboolean
-nm_platform_ethtool_set_ring(NMPlatform *self, int ifindex, const NMEthtoolRingState *ring)
-{
-    _CHECK_SELF_NETNS(self, klass, netns, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-
-    return nmp_utils_ethtool_set_ring(ifindex, ring);
-}
-
-/*****************************************************************************/
-
-const NMDedupMultiHeadEntry *
-nm_platform_lookup_all(NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj)
-{
-    return nmp_cache_lookup_all(nm_platform_get_cache(self), cache_id_type, obj);
-}
-
-const NMDedupMultiEntry *
-nm_platform_lookup_entry(NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj)
-{
-    return nmp_cache_lookup_entry_with_idx_type(nm_platform_get_cache(self), cache_id_type, obj);
-}
-
-const NMDedupMultiHeadEntry *
-nm_platform_lookup(NMPlatform *self, const NMPLookup *lookup)
-{
-    return nmp_cache_lookup(nm_platform_get_cache(self), lookup);
-}
-
-gboolean
-nm_platform_lookup_predicate_routes_main(const NMPObject *obj, gpointer user_data)
-{
-    nm_assert(
-        NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
-    return nm_platform_route_table_is_main(
-        nm_platform_ip_route_get_effective_table(&obj->ip_route));
-}
-
-gboolean
-nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel(const NMPObject *obj,
-                                                            gpointer         user_data)
-{
-    nm_assert(
-        NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
-    return nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&obj->ip_route))
-           && obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL;
-}
-
-/**
- * nm_platform_lookup_clone:
- * @self:
- * @lookup:
- * @predicate: if given, only objects for which @predicate returns %TRUE are included
- *   in the result.
- * @user_data: user data for @predicate
- *
- * Returns the result of lookup in a GPtrArray. The result array contains
- * references objects from the cache, its destroy function will unref them.
- *
- * The user must unref the GPtrArray, which will also unref the NMPObject
- * elements.
- *
- * The elements in the array *must* not be modified.
- *
- * Returns: the result of the lookup.
- */
-GPtrArray *
-nm_platform_lookup_clone(NMPlatform *           self,
-                         const NMPLookup *      lookup,
-                         NMPObjectPredicateFunc predicate,
-                         gpointer               user_data)
-{
-    return nm_dedup_multi_objs_to_ptr_array_head(nm_platform_lookup(self, lookup),
-                                                 (NMDedupMultiFcnSelectPredicate) predicate,
-                                                 user_data);
-}
-
-void
-nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen)
-{
-    nm_assert(plen <= 32);
-
-    addr->address      = address;
-    addr->peer_address = address;
-    addr->plen         = plen;
-}
-
-const struct in6_addr *
-nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr)
-{
-    if (IN6_IS_ADDR_UNSPECIFIED(&addr->peer_address)
-        || IN6_ARE_ADDR_EQUAL(&addr->peer_address, &addr->address))
-        return &addr->address;
-    return &addr->peer_address;
-}
-
-gboolean
-nm_platform_ip6_address_match(const NMPlatformIP6Address *addr, NMPlatformMatchFlags match_flag)
-{
-    nm_assert(!NM_FLAGS_ANY(
-        match_flag,
-        ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)));
-    nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY));
-    nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY));
-
-    if (IN6_IS_ADDR_LINKLOCAL(&addr->address)) {
-        if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL))
-            return FALSE;
-    } else {
-        if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL))
-            return FALSE;
-    }
-
-    if (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_DADFAILED)) {
-        if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED))
-            return FALSE;
-    } else if (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TENTATIVE)
-               && !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
-        if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE))
-            return FALSE;
-    } else {
-        if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL))
-            return FALSE;
-    }
-
-    return TRUE;
-}
-
-gboolean
-nm_platform_ip4_address_add(NMPlatform *self,
-                            int         ifindex,
-                            in_addr_t   address,
-                            guint8      plen,
-                            in_addr_t   peer_address,
-                            in_addr_t   broadcast_address,
-                            guint32     lifetime,
-                            guint32     preferred,
-                            guint32     flags,
-                            const char *label)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(plen <= 32, FALSE);
-    g_return_val_if_fail(lifetime > 0, FALSE);
-    g_return_val_if_fail(preferred <= lifetime, FALSE);
-    g_return_val_if_fail(!label || strlen(label) < sizeof(((NMPlatformIP4Address *) NULL)->label),
-                         FALSE);
-
-    if (_LOGD_ENABLED()) {
-        NMPlatformIP4Address addr;
-
-        addr = (NMPlatformIP4Address){
-            .ifindex           = ifindex,
-            .address           = address,
-            .peer_address      = peer_address,
-            .plen              = plen,
-            .timestamp         = 0, /* set it at zero, which to_string will treat as *now* */
-            .lifetime          = lifetime,
-            .preferred         = preferred,
-            .n_ifa_flags       = flags,
-            .broadcast_address = broadcast_address,
-            .use_ip4_broadcast_address = TRUE,
-        };
-        if (label)
-            g_strlcpy(addr.label, label, sizeof(addr.label));
-
-        _LOG3D("address: adding or updating IPv4 address: %s",
-               nm_platform_ip4_address_to_string(&addr, NULL, 0));
-    }
-    return klass->ip4_address_add(self,
-                                  ifindex,
-                                  address,
-                                  plen,
-                                  peer_address,
-                                  broadcast_address,
-                                  lifetime,
-                                  preferred,
-                                  flags,
-                                  label);
-}
-
-gboolean
-nm_platform_ip6_address_add(NMPlatform *    self,
-                            int             ifindex,
-                            struct in6_addr address,
-                            guint8          plen,
-                            struct in6_addr peer_address,
-                            guint32         lifetime,
-                            guint32         preferred,
-                            guint32         flags)
-{
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(plen <= 128, FALSE);
-    g_return_val_if_fail(lifetime > 0, FALSE);
-    g_return_val_if_fail(preferred <= lifetime, FALSE);
-
-    if (_LOGD_ENABLED()) {
-        NMPlatformIP6Address addr = {0};
-
-        addr.ifindex      = ifindex;
-        addr.address      = address;
-        addr.peer_address = peer_address;
-        addr.plen         = plen;
-        addr.timestamp    = 0; /* set it to zero, which to_string will treat as *now* */
-        addr.lifetime     = lifetime;
-        addr.preferred    = preferred;
-        addr.n_ifa_flags  = flags;
-
-        _LOG3D("address: adding or updating IPv6 address: %s",
-               nm_platform_ip6_address_to_string(&addr, NULL, 0));
-    }
-    return klass
-        ->ip6_address_add(self, ifindex, address, plen, peer_address, lifetime, preferred, flags);
-}
-
-gboolean
-nm_platform_ip4_address_delete(NMPlatform *self,
-                               int         ifindex,
-                               in_addr_t   address,
-                               guint8      plen,
-                               in_addr_t   peer_address)
-{
-    char str_dev[TO_STRING_DEV_BUF_SIZE];
-    char b1[NM_UTILS_INET_ADDRSTRLEN];
-    char b2[NM_UTILS_INET_ADDRSTRLEN];
-    char str_peer[INET_ADDRSTRLEN + 50];
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(plen <= 32, FALSE);
-
-    _LOG3D("address: deleting IPv4 address %s/%d, %s%s",
-           _nm_utils_inet4_ntop(address, b1),
-           plen,
-           peer_address != address
-               ? nm_sprintf_buf(str_peer, "peer %s, ", _nm_utils_inet4_ntop(peer_address, b2))
-               : "",
-           _to_string_dev(self, ifindex, str_dev, sizeof(str_dev)));
-    return klass->ip4_address_delete(self, ifindex, address, plen, peer_address);
-}
-
-gboolean
-nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen)
-{
-    char str_dev[TO_STRING_DEV_BUF_SIZE];
-    char sbuf[NM_UTILS_INET_ADDRSTRLEN];
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(ifindex > 0, FALSE);
-    g_return_val_if_fail(plen <= 128, FALSE);
-
-    _LOG3D("address: deleting IPv6 address %s/%d, %s",
-           _nm_utils_inet6_ntop(&address, sbuf),
-           plen,
-           _to_string_dev(self, ifindex, str_dev, sizeof(str_dev)));
-    return klass->ip6_address_delete(self, ifindex, address, plen);
-}
-
-const NMPlatformIP4Address *
-nm_platform_ip4_address_get(NMPlatform *self,
-                            int         ifindex,
-                            in_addr_t   address,
-                            guint8      plen,
-                            in_addr_t   peer_address)
-{
-    NMPObject        obj_id;
-    const NMPObject *obj;
-
-    _CHECK_SELF(self, klass, NULL);
-
-    g_return_val_if_fail(plen <= 32, NULL);
-
-    nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, address, plen, peer_address);
-    obj = nmp_cache_lookup_obj(nm_platform_get_cache(self), &obj_id);
-    nm_assert(!obj || nmp_object_is_visible(obj));
-    return NMP_OBJECT_CAST_IP4_ADDRESS(obj);
-}
-
-const NMPlatformIP6Address *
-nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address)
-{
-    NMPObject        obj_id;
-    const NMPObject *obj;
-
-    _CHECK_SELF(self, klass, NULL);
-
-    nm_assert(address);
-
-    nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, address);
-    obj = nmp_cache_lookup_obj(nm_platform_get_cache(self), &obj_id);
-    nm_assert(!obj || nmp_object_is_visible(obj));
-    return NMP_OBJECT_CAST_IP6_ADDRESS(obj);
-}
-
-static gboolean
-_addr_array_clean_expired(int          addr_family,
-                          int          ifindex,
-                          GPtrArray *  array,
-                          guint32      now,
-                          GHashTable **idx)
-{
-    guint    i;
-    gboolean any_addrs = FALSE;
-
-    nm_assert_addr_family(addr_family);
-    nm_assert(ifindex > 0);
-    nm_assert(now > 0);
-
-    if (!array)
-        return FALSE;
-
-    /* remove all addresses that are already expired. */
-    for (i = 0; i < array->len; i++) {
-        const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS(array->pdata[i]);
-
-#if NM_MORE_ASSERTS > 10
-        nm_assert(a);
-        nm_assert(a->ifindex == ifindex);
-        {
-            const NMPObject *o = NMP_OBJECT_UP_CAST(a);
-            guint            j;
-
-            nm_assert(NMP_OBJECT_GET_CLASS(o)->addr_family == addr_family);
-            for (j = i + 1; j < array->len; j++) {
-                const NMPObject *o2 = array->pdata[j];
-
-                nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_GET_TYPE(o2));
-                nm_assert(!nmp_object_id_equal(o, o2));
-            }
-        }
-#endif
-
-        if (!NM_IS_IPv4(addr_family) && NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_TEMPORARY)) {
-            /* temporary addresses are never added explicitly by NetworkManager but
-             * kernel adds them via mngtempaddr flag.
-             *
-             * We drop them from this list. */
-            goto clear_and_next;
-        }
-
-        if (!nm_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, now, NULL))
-            goto clear_and_next;
-
-        if (idx) {
-            if (G_UNLIKELY(!*idx)) {
-                *idx = g_hash_table_new((GHashFunc) nmp_object_id_hash,
-                                        (GEqualFunc) nmp_object_id_equal);
-            }
-            if (!g_hash_table_add(*idx, (gpointer) NMP_OBJECT_UP_CAST(a)))
-                nm_assert_not_reached();
-        }
-        any_addrs = TRUE;
-        continue;
-
-clear_and_next:
-        nmp_object_unref(g_steal_pointer(&array->pdata[i]));
-    }
-
-    return any_addrs;
-}
-
-static gboolean
-ip4_addr_subnets_is_plain_address(const GPtrArray *addresses, gconstpointer needle)
-{
-    return needle >= (gconstpointer) &addresses->pdata[0]
-           && needle < (gconstpointer) &addresses->pdata[addresses->len];
-}
-
-static const NMPObject **
-ip4_addr_subnets_addr_list_get(const GPtrArray *addr_list, guint idx)
-{
-    nm_assert(addr_list);
-    nm_assert(addr_list->len > 1);
-    nm_assert(idx < addr_list->len);
-    nm_assert(addr_list->pdata[idx]);
-    nm_assert(!(*((gpointer *) addr_list->pdata[idx]))
-              || NMP_OBJECT_CAST_IP4_ADDRESS(*((gpointer *) addr_list->pdata[idx])));
-    nm_assert(idx == 0 || ip4_addr_subnets_addr_list_get(addr_list, idx - 1));
-    return addr_list->pdata[idx];
-}
-
-static void
-ip4_addr_subnets_destroy_index(GHashTable *subnets, const GPtrArray *addresses)
-{
-    GHashTableIter iter;
-    gpointer       p;
-
-    if (!subnets)
-        return;
-
-    g_hash_table_iter_init(&iter, subnets);
-    while (g_hash_table_iter_next(&iter, NULL, &p)) {
-        if (!ip4_addr_subnets_is_plain_address(addresses, p))
-            g_ptr_array_free((GPtrArray *) p, TRUE);
-    }
-
-    g_hash_table_unref(subnets);
-}
-
-static GHashTable *
-ip4_addr_subnets_build_index(const GPtrArray *addresses,
-                             gboolean         consider_flags,
-                             gboolean         full_index)
-{
-    GHashTable *subnets;
-    guint       i;
-
-    nm_assert(addresses && addresses->len);
-
-    subnets = g_hash_table_new(nm_direct_hash, NULL);
-
-    /* Build a hash table of all addresses per subnet */
-    for (i = 0; i < addresses->len; i++) {
-        const NMPlatformIP4Address *address;
-        gpointer                    p_address;
-        GPtrArray *                 addr_list;
-        guint32                     net;
-        int                         position;
-        gpointer                    p;
-
-        if (!addresses->pdata[i])
-            continue;
-
-        p_address = &addresses->pdata[i];
-        address   = NMP_OBJECT_CAST_IP4_ADDRESS(addresses->pdata[i]);
-
-        net = address->address & _nm_utils_ip4_prefix_to_netmask(address->plen);
-        if (!g_hash_table_lookup_extended(subnets, GUINT_TO_POINTER(net), NULL, &p)) {
-            g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address);
-            continue;
-        }
-        nm_assert(p);
-
-        if (full_index) {
-            if (ip4_addr_subnets_is_plain_address(addresses, p)) {
-                addr_list = g_ptr_array_new();
-                g_hash_table_insert(subnets, GUINT_TO_POINTER(net), addr_list);
-                g_ptr_array_add(addr_list, p);
-            } else
-                addr_list = p;
-
-            if (!consider_flags || NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_SECONDARY))
-                position = -1; /* append */
-            else
-                position = 0; /* prepend */
-            g_ptr_array_insert(addr_list, position, p_address);
-        } else {
-            /* we only care about the primary. No need to track the secondaries
-             * as a GPtrArray. */
-            nm_assert(ip4_addr_subnets_is_plain_address(addresses, p));
-            if (consider_flags && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_SECONDARY)) {
-                g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address);
-            }
-        }
-    }
-
-    return subnets;
-}
-
-/**
- * ip4_addr_subnets_is_secondary:
- * @address: an address
- * @subnets: the hash table mapping subnets to addresses
- * @addresses: array of addresses in the hash table
- * @out_addr_list: array of addresses belonging to the same subnet
- *
- * Checks whether @address is secondary and returns in @out_addr_list the list of addresses
- * belonging to the same subnet, if it contains other elements.
- *
- * Returns: %TRUE if the address is secondary, %FALSE otherwise
- */
-static gboolean
-ip4_addr_subnets_is_secondary(const NMPObject * address,
-                              GHashTable *      subnets,
-                              const GPtrArray * addresses,
-                              const GPtrArray **out_addr_list)
-{
-    const NMPlatformIP4Address *a;
-    const GPtrArray *           addr_list;
-    gconstpointer               p;
-    guint32                     net;
-    const NMPObject **          o;
-
-    a = NMP_OBJECT_CAST_IP4_ADDRESS(address);
-
-    net = a->address & _nm_utils_ip4_prefix_to_netmask(a->plen);
-    p   = g_hash_table_lookup(subnets, GUINT_TO_POINTER(net));
-    nm_assert(p);
-    if (!ip4_addr_subnets_is_plain_address(addresses, p)) {
-        addr_list = p;
-        nm_assert(addr_list->len > 1);
-        NM_SET_OUT(out_addr_list, addr_list);
-        o = ip4_addr_subnets_addr_list_get(addr_list, 0);
-        nm_assert(o && *o);
-        if (*o != address)
-            return TRUE;
-    } else {
-        NM_SET_OUT(out_addr_list, NULL);
-        return address != *((gconstpointer *) p);
-    }
-    return FALSE;
-}
-
-typedef enum {
-    IP6_ADDR_SCOPE_LOOPBACK,
-    IP6_ADDR_SCOPE_LINKLOCAL,
-    IP6_ADDR_SCOPE_SITELOCAL,
-    IP6_ADDR_SCOPE_OTHER,
-} IP6AddrScope;
-
-static IP6AddrScope
-ip6_address_scope(const NMPlatformIP6Address *a)
-{
-    if (IN6_IS_ADDR_LOOPBACK(&a->address))
-        return IP6_ADDR_SCOPE_LOOPBACK;
-    if (IN6_IS_ADDR_LINKLOCAL(&a->address))
-        return IP6_ADDR_SCOPE_LINKLOCAL;
-    if (IN6_IS_ADDR_SITELOCAL(&a->address))
-        return IP6_ADDR_SCOPE_SITELOCAL;
-    return IP6_ADDR_SCOPE_OTHER;
-}
-
-static int
-ip6_address_scope_cmp(gconstpointer p_a, gconstpointer p_b, gpointer increasing)
-{
-    const NMPlatformIP6Address *a;
-    const NMPlatformIP6Address *b;
-
-    if (!increasing)
-        NM_SWAP(&p_a, &p_b);
-
-    a = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_a);
-    b = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_b);
-
-    NM_CMP_DIRECT(ip6_address_scope(a), ip6_address_scope(b));
-    return 0;
-}
-
-/**
- * nm_platform_ip_address_sync:
- * @self: platform instance
- * @addr_family: the address family AF_INET or AF_INET6.
- * @ifindex: Interface index
- * @known_addresses: List of addresses. The list will be modified and only
- *   addresses that were successfully added will be kept in the list.
- *   That means, expired addresses and addresses that could not be added
- *   will be dropped.
- *   Hence, the input argument @known_addresses is also an output argument
- *   telling which addresses were successfully added.
- *   Addresses are removed by unrefing the instance via nmp_object_unref()
- *   and leaving a NULL tombstone.
- * @addresses_prune: (allow-none): the list of addresses to delete.
- *   If platform has such an address configured, it will be deleted
- *   at the beginning of the sync. Note that the array will be modified
- *   by the function.
- *   Note that the addresses must be properly sorted, by their priority.
- *   Create this list with nm_platform_ip_address_get_prune_list() which
- *   gets the sorting right.
- *
- * A convenience function to synchronize addresses for a specific interface
- * with the least possible disturbance. It simply removes addresses that are
- * not listed and adds addresses that are.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-nm_platform_ip_address_sync(NMPlatform *self,
-                            int         addr_family,
-                            int         ifindex,
-                            GPtrArray * known_addresses,
-                            GPtrArray * addresses_prune)
-{
-    const gint32       now                             = nm_utils_get_monotonic_timestamp_sec();
-    const int          IS_IPv4                         = NM_IS_IPv4(addr_family);
-    gs_unref_hashtable GHashTable *known_addresses_idx = NULL;
-    GPtrArray *                    plat_addresses;
-    GHashTable *                   known_subnets = NULL;
-    guint32                        ifa_flags;
-    guint                          i_plat;
-    guint                          i_know;
-    guint                          i;
-    guint                          j;
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    /* The order we want to enforce is only among addresses with the same
-     * scope, as the kernel keeps addresses sorted by scope. Therefore,
-     * apply the same sorting to known addresses, so that we don't try to
-     * unnecessary change the order of addresses with different scopes. */
-    if (!IS_IPv4) {
-        if (known_addresses)
-            g_ptr_array_sort_with_data(known_addresses,
-                                       ip6_address_scope_cmp,
-                                       GINT_TO_POINTER(TRUE));
-    }
-
-    if (!_addr_array_clean_expired(addr_family,
-                                   ifindex,
-                                   known_addresses,
-                                   now,
-                                   &known_addresses_idx))
-        known_addresses = NULL;
-
-    /* @plat_addresses must be sorted in decreasing priority order (highest priority addresses first), contrary to
-     * @known_addresses which is in increasing priority order (lowest priority addresses first). */
-    plat_addresses = addresses_prune;
-
-    if (nm_g_ptr_array_len(plat_addresses) > 0) {
-        /* Delete unknown addresses */
-        if (IS_IPv4) {
-            GHashTable *plat_subnets;
-
-            plat_subnets = ip4_addr_subnets_build_index(plat_addresses, TRUE, TRUE);
-
-            for (i = 0; i < plat_addresses->len; i++) {
-                const NMPObject *           plat_obj;
-                const NMPlatformIP4Address *plat_address;
-                const GPtrArray *           addr_list;
-
-                plat_obj = plat_addresses->pdata[i];
-                if (!plat_obj) {
-                    /* Already deleted */
-                    continue;
-                }
-
-                plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj);
-
-                if (known_addresses) {
-                    const NMPObject *o;
-
-                    o = g_hash_table_lookup(known_addresses_idx, plat_obj);
-                    if (o) {
-                        gboolean secondary;
-
-                        if (!known_subnets)
-                            known_subnets =
-                                ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE);
-
-                        secondary =
-                            ip4_addr_subnets_is_secondary(o, known_subnets, known_addresses, NULL);
-                        if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) {
-                            /* if we have an existing known-address, with matching secondary role,
-                             * do not delete the platform-address. */
-                            continue;
-                        }
-                    }
-                }
-
-                nm_platform_ip4_address_delete(self,
-                                               ifindex,
-                                               plat_address->address,
-                                               plat_address->plen,
-                                               plat_address->peer_address);
-
-                if (!ip4_addr_subnets_is_secondary(plat_obj,
-                                                   plat_subnets,
-                                                   plat_addresses,
-                                                   &addr_list)
-                    && addr_list) {
-                    /* If we just deleted a primary addresses and there were
-                     * secondary ones the kernel can do two things, depending on
-                     * version and sysctl setting: delete also secondary addresses
-                     * or promote a secondary to primary. Ensure that secondary
-                     * addresses are deleted, so that we can start with a clean
-                     * slate and add addresses in the right order. */
-                    for (j = 1; j < addr_list->len; j++) {
-                        const NMPObject **o;
-
-                        o = ip4_addr_subnets_addr_list_get(addr_list, j);
-                        nm_assert(o);
-
-                        if (*o) {
-                            const NMPlatformIP4Address *a;
-
-                            a = NMP_OBJECT_CAST_IP4_ADDRESS(*o);
-                            nm_platform_ip4_address_delete(self,
-                                                           ifindex,
-                                                           a->address,
-                                                           a->plen,
-                                                           a->peer_address);
-                            nmp_object_unref(*o);
-                            *o = NULL;
-                        }
-                    }
-                }
-            }
-            ip4_addr_subnets_destroy_index(plat_subnets, plat_addresses);
-        } else {
-            guint        known_addresses_len;
-            IP6AddrScope cur_scope;
-            gboolean     delete_remaining_addrs;
-
-            g_ptr_array_sort_with_data(plat_addresses,
-                                       ip6_address_scope_cmp,
-                                       GINT_TO_POINTER(FALSE));
-
-            known_addresses_len = known_addresses ? known_addresses->len : 0;
-
-            /* First, compare every address whether it is still a "known address", that is, whether
-             * to keep it or to delete it.
-             *
-             * If we don't find a matching valid address in @known_addresses, we will delete
-             * plat_addr.
-             *
-             * Certain addresses, like temporary addresses, are ignored by this function
-             * if not run with full_sync. These addresses are usually not managed by NetworkManager
-             * directly, or at least, they are not managed via nm_platform_ip6_address_sync().
-             * Only in full_sync mode, we really want to get rid of them (usually, when we take
-             * the interface down).
-             *
-             * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */
-            for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) {
-                const NMPObject *           plat_obj = plat_addresses->pdata[i_plat];
-                const NMPObject *           know_obj;
-                const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj);
-
-                if (known_addresses_idx) {
-                    know_obj = g_hash_table_lookup(known_addresses_idx, plat_obj);
-                    if (know_obj
-                        && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS(know_obj)->plen) {
-                        /* technically, plen is not part of the ID for IPv6 addresses and thus
-                         * @plat_addr is essentially the same address as @know_addr (regrading
-                         * its identity, not its other attributes).
-                         * However, we cannot modify an existing addresses' plen without
-                         * removing and readding it. Thus, only keep plat_addr, if the plen
-                         * matches.
-                         *
-                         * keep this one, and continue */
-                        continue;
-                    }
-                }
-
-                nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen);
-                nmp_object_unref(g_steal_pointer(&plat_addresses->pdata[i_plat]));
-            }
-
-            /* Next, we must preserve the priority of the routes. That is, source address
-             * selection will choose addresses in the order as they are reported by kernel.
-             * Note that the order in @plat_addresses of the remaining matches is highest
-             * priority first.
-             * We need to compare this to the order of addresses with same scope in
-             * @known_addresses (which has lowest priority first).
-             *
-             * If we find a first discrepancy, we need to delete all remaining addresses
-             * with same scope from that point on, because below we must re-add all the
-             * addresses in the right order to get their priority right. */
-            cur_scope              = IP6_ADDR_SCOPE_LOOPBACK;
-            delete_remaining_addrs = FALSE;
-            i_plat                 = plat_addresses->len;
-            i_know                 = 0;
-            while (i_plat > 0) {
-                const NMPlatformIP6Address *plat_addr =
-                    NMP_OBJECT_CAST_IP6_ADDRESS(plat_addresses->pdata[--i_plat]);
-                IP6AddrScope plat_scope;
-
-                if (!plat_addr)
-                    continue;
-
-                plat_scope = ip6_address_scope(plat_addr);
-                if (cur_scope != plat_scope) {
-                    nm_assert(cur_scope < plat_scope);
-                    delete_remaining_addrs = FALSE;
-                    cur_scope              = plat_scope;
-                }
-
-                if (!delete_remaining_addrs) {
-                    delete_remaining_addrs = TRUE;
-                    for (; i_know < known_addresses_len; i_know++) {
-                        const NMPlatformIP6Address *know_addr =
-                            NMP_OBJECT_CAST_IP6_ADDRESS(known_addresses->pdata[i_know]);
-                        IP6AddrScope know_scope;
-
-                        if (!know_addr)
-                            continue;
-
-                        know_scope = ip6_address_scope(know_addr);
-                        if (know_scope < plat_scope)
-                            continue;
-
-                        if (IN6_ARE_ADDR_EQUAL(&plat_addr->address, &know_addr->address)) {
-                            /* we have a match. Mark address as handled. */
-                            i_know++;
-                            delete_remaining_addrs = FALSE;
-                            goto next_plat;
-                        }
-
-                        /* plat_address has no match. Now delete_remaining_addrs is TRUE and we will
-                         * delete all the remaining addresses with cur_scope. */
-                        break;
-                    }
-                }
-
-                nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen);
-next_plat:;
-            }
-        }
-    }
-
-    if (!known_addresses)
-        return TRUE;
-
-    if (IS_IPv4)
-        ip4_addr_subnets_destroy_index(known_subnets, known_addresses);
-
-    ifa_flags = nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)
-                    ? IFA_F_NOPREFIXROUTE
-                    : 0;
-
-    /* Add missing addresses. New addresses are added by kernel with top
-     * priority.
-     */
-    for (i_know = 0; i_know < known_addresses->len; i_know++) {
-        const NMPlatformIPXAddress *known_address;
-        const NMPObject *           o;
-        guint32                     lifetime;
-        guint32                     preferred;
-
-        o = known_addresses->pdata[i_know];
-        if (!o)
-            continue;
-
-        nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4));
-
-        known_address = NMP_OBJECT_CAST_IPX_ADDRESS(o);
-
-        lifetime = nm_utils_lifetime_get(known_address->ax.timestamp,
-                                         known_address->ax.lifetime,
-                                         known_address->ax.preferred,
-                                         now,
-                                         &preferred);
-        nm_assert(lifetime > 0);
-
-        if (IS_IPv4) {
-            if (!nm_platform_ip4_address_add(
-                    self,
-                    ifindex,
-                    known_address->a4.address,
-                    known_address->a4.plen,
-                    known_address->a4.peer_address,
-                    nm_platform_ip4_broadcast_address_from_addr(&known_address->a4),
-                    lifetime,
-                    preferred,
-                    ifa_flags,
-                    known_address->a4.label)) {
-                /* ignore error, for unclear reasons. */
-            }
-        } else {
-            if (!nm_platform_ip6_address_add(self,
-                                             ifindex,
-                                             known_address->a6.address,
-                                             known_address->a6.plen,
-                                             known_address->a6.peer_address,
-                                             lifetime,
-                                             preferred,
-                                             ifa_flags | known_address->a6.n_ifa_flags))
-                return FALSE;
-        }
-    }
-
-    return TRUE;
-}
-
-gboolean
-nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex)
-{
-    gboolean success = TRUE;
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
-
-    if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET))
-        success &= nm_platform_ip4_address_sync(self, ifindex, NULL);
-    if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6))
-        success &= nm_platform_ip6_address_sync(self, ifindex, NULL, TRUE);
-    return success;
-}
-
-/*****************************************************************************/
-
-static gboolean
-_err_inval_due_to_ipv6_tentative_pref_src(NMPlatform *self, const NMPObject *obj)
-{
-    const NMPlatformIP6Route *  r;
-    const NMPlatformIP6Address *a;
-
-    nm_assert(NM_IS_PLATFORM(self));
-    nm_assert(NMP_OBJECT_IS_VALID(obj));
-
-    /* trying to add an IPv6 route with pref-src fails, if the address is
-     * still tentative (rh#1452684). We need to hack around that.
-     *
-     * Detect it, by guessing whether that's the case. */
-
-    if (NMP_OBJECT_GET_TYPE(obj) != NMP_OBJECT_TYPE_IP6_ROUTE)
-        return FALSE;
-
-    r = NMP_OBJECT_CAST_IP6_ROUTE(obj);
-
-    /* we only allow this workaround for routes added manually by the user. */
-    if (r->rt_source != NM_IP_CONFIG_SOURCE_USER)
-        return FALSE;
-
-    if (IN6_IS_ADDR_UNSPECIFIED(&r->pref_src))
-        return FALSE;
-
-    a = nm_platform_ip6_address_get(self, r->ifindex, &r->pref_src);
-    if (!a)
-        return FALSE;
-    if (!NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_TENTATIVE)
-        || NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_DADFAILED))
-        return FALSE;
-
-    return TRUE;
-}
-
-GPtrArray *
-nm_platform_ip_address_get_prune_list(NMPlatform *self,
-                                      int         addr_family,
-                                      int         ifindex,
-                                      gboolean    exclude_ipv6_temporary_addrs)
-{
-    const int                    IS_IPv4 = NM_IS_IPv4(addr_family);
-    const NMDedupMultiHeadEntry *head_entry;
-    NMPLookup                    lookup;
-    GPtrArray *                  result;
-    CList *                      iter;
-
-    nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(NM_IS_IPv4(addr_family)), ifindex);
-
-    head_entry = nm_platform_lookup(self, &lookup);
-
-    if (!head_entry)
-        return NULL;
-
-    result = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nmp_object_unref);
-
-    c_list_for_each (iter, &head_entry->lst_entries_head) {
-        const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj;
-
-        if (!IS_IPv4) {
-            if (exclude_ipv6_temporary_addrs
-                && NM_FLAGS_HAS(NMP_OBJECT_CAST_IP_ADDRESS(obj)->n_ifa_flags, IFA_F_TEMPORARY))
-                continue;
-        }
-
-        g_ptr_array_add(result, (gpointer) nmp_object_ref(obj));
-    }
-
-    if (result->len == 0) {
-        g_ptr_array_unref(result);
-        return NULL;
-    }
-    return result;
-}
-
-GPtrArray *
-nm_platform_ip_route_get_prune_list(NMPlatform *           self,
-                                    int                    addr_family,
-                                    int                    ifindex,
-                                    NMIPRouteTableSyncMode route_table_sync)
-{
-    NMPLookup                    lookup;
-    GPtrArray *                  routes_prune;
-    const NMDedupMultiHeadEntry *head_entry;
-    CList *                      iter;
-
-    nm_assert(NM_IS_PLATFORM(self));
-    nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
-    nm_assert(NM_IN_SET(route_table_sync,
-                        NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
-                        NM_IP_ROUTE_TABLE_SYNC_MODE_FULL,
-                        NM_IP_ROUTE_TABLE_SYNC_MODE_ALL));
-
-    nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), ifindex);
-    head_entry = nm_platform_lookup(self, &lookup);
-    if (!head_entry)
-        return NULL;
-
-    routes_prune = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref);
-
-    c_list_for_each (iter, &head_entry->lst_entries_head) {
-        const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj;
-
-        if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_FULL) {
-            if (nm_platform_ip_route_get_effective_table(NMP_OBJECT_CAST_IP_ROUTE(obj))
-                == RT_TABLE_LOCAL)
-                continue;
-        } else if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN) {
-            if (!nm_platform_route_table_is_main(
-                    nm_platform_ip_route_get_effective_table(NMP_OBJECT_CAST_IP_ROUTE(obj))))
-                continue;
-        } else
-            nm_assert(route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_ALL);
-
-        g_ptr_array_add(routes_prune, (gpointer) nmp_object_ref(obj));
-    }
-
-    if (routes_prune->len == 0) {
-        g_ptr_array_unref(routes_prune);
-        return NULL;
-    }
-    return routes_prune;
-}
-
-/**
- * nm_platform_ip_route_sync:
- * @self: the #NMPlatform instance.
- * @addr_family: AF_INET or AF_INET6.
- * @ifindex: the @ifindex for which the routes are to be added.
- * @routes: (allow-none): a list of routes to configure. Must contain
- *   NMPObject instances of routes, according to @addr_family.
- * @routes_prune: (allow-none): the list of routes to delete.
- *   If platform has such a route configured, it will be deleted
- *   at the end of the operation. Note that if @routes contains
- *   the same route, then it will not be deleted. @routes overrules
- *   @routes_prune list.
- * @out_temporary_not_available: (allow-none) (out): routes that could
- *   currently not be synced. The caller shall keep them and try later again.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-nm_platform_ip_route_sync(NMPlatform *self,
-                          int         addr_family,
-                          int         ifindex,
-                          GPtrArray * routes,
-                          GPtrArray * routes_prune,
-                          GPtrArray **out_temporary_not_available)
-{
-    const int                    IS_IPv4 = NM_IS_IPv4(addr_family);
-    const NMPlatformVTableRoute *vt;
-    gs_unref_hashtable GHashTable *routes_idx = NULL;
-    const NMPObject *              conf_o;
-    const NMDedupMultiEntry *      plat_entry;
-    guint                          i;
-    int                            i_type;
-    gboolean                       success = TRUE;
-    char                           sbuf1[sizeof(_nm_utils_to_string_buffer)];
-    char                           sbuf2[sizeof(_nm_utils_to_string_buffer)];
-
-    nm_assert(NM_IS_PLATFORM(self));
-    nm_assert(ifindex > 0);
-
-    vt = &nm_platform_vtable_route.vx[IS_IPv4];
-
-    for (i_type = 0; routes && i_type < 2; i_type++) {
-        for (i = 0; i < routes->len; i++) {
-            int      r, r2;
-            gboolean gateway_route_added = FALSE;
-
-            conf_o = routes->pdata[i];
-
-#define VTABLE_IS_DEVICE_ROUTE(vt, o)                          \
-    (vt->is_ip4 ? (NMP_OBJECT_CAST_IP4_ROUTE(o)->gateway == 0) \
-                : IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(o)->gateway))
-
-            if ((i_type == 0 && !VTABLE_IS_DEVICE_ROUTE(vt, conf_o))
-                || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE(vt, conf_o))) {
-                /* we add routes in two runs over @i_type.
-                 *
-                 * First device routes, then gateway routes. */
-                continue;
-            }
-
-            if (!routes_idx) {
-                routes_idx = g_hash_table_new((GHashFunc) nmp_object_id_hash,
-                                              (GEqualFunc) nmp_object_id_equal);
-            }
-            if (!g_hash_table_insert(routes_idx, (gpointer) conf_o, (gpointer) conf_o)) {
-                _LOG3D("route-sync: skip adding duplicate route %s",
-                       nmp_object_to_string(conf_o,
-                                            NMP_OBJECT_TO_STRING_PUBLIC,
-                                            sbuf1,
-                                            sizeof(sbuf1)));
-                continue;
-            }
-
-            if (!IS_IPv4
-                && nm_platform_ip6_route_get_effective_metric(NMP_OBJECT_CAST_IP6_ROUTE(conf_o))
-                       == 0) {
-                /* User space cannot add routes with metric 0. However, kernel can, and we might track such
-                 * routes in @route as they are present external. Skip them silently. */
-                continue;
-            }
-
-            plat_entry = nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, conf_o);
-            if (plat_entry) {
-                const NMPObject *plat_o;
-
-                plat_o = plat_entry->obj;
-
-                if (vt->route_cmp(NMP_OBJECT_CAST_IPX_ROUTE(conf_o),
-                                  NMP_OBJECT_CAST_IPX_ROUTE(plat_o),
-                                  NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
-                    == 0)
-                    continue;
-
-                /* we need to replace the existing route with a (slightly) different
-                 * one. Delete it first. */
-                if (!nm_platform_object_delete(self, plat_o)) {
-                    /* ignore error. */
-                }
-            }
-
-sync_route_add:
-            r = nm_platform_ip_route_add(self,
-                                         NMP_NLM_FLAG_APPEND
-                                             | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE,
-                                         conf_o);
-            if (r < 0) {
-                if (r == -EEXIST) {
-                    /* Don't fail for EEXIST. It's not clear that the existing route
-                     * is identical to the one that we were about to add. However,
-                     * above we should have deleted conflicting (non-identical) routes. */
-                    if (_LOGD_ENABLED()) {
-                        plat_entry =
-                            nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, conf_o);
-                        if (!plat_entry) {
-                            _LOG3D("route-sync: adding route %s failed with EEXIST, however we "
-                                   "cannot find such a route",
-                                   nmp_object_to_string(conf_o,
-                                                        NMP_OBJECT_TO_STRING_PUBLIC,
-                                                        sbuf1,
-                                                        sizeof(sbuf1)));
-                        } else if (vt->route_cmp(NMP_OBJECT_CAST_IPX_ROUTE(conf_o),
-                                                 NMP_OBJECT_CAST_IPX_ROUTE(plat_entry->obj),
-                                                 NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
-                                   != 0) {
-                            _LOG3D("route-sync: adding route %s failed due to existing "
-                                   "(different!) route %s",
-                                   nmp_object_to_string(conf_o,
-                                                        NMP_OBJECT_TO_STRING_PUBLIC,
-                                                        sbuf1,
-                                                        sizeof(sbuf1)),
-                                   nmp_object_to_string(plat_entry->obj,
-                                                        NMP_OBJECT_TO_STRING_PUBLIC,
-                                                        sbuf2,
-                                                        sizeof(sbuf2)));
-                        }
-                    }
-                } else if (NMP_OBJECT_CAST_IP_ROUTE(conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) {
-                    _LOG3D("route-sync: ignore failure to add IPv%c route: %s: %s",
-                           vt->is_ip4 ? '4' : '6',
-                           nmp_object_to_string(conf_o,
-                                                NMP_OBJECT_TO_STRING_PUBLIC,
-                                                sbuf1,
-                                                sizeof(sbuf1)),
-                           nm_strerror(r));
-                } else if (r == -EINVAL && out_temporary_not_available
-                           && _err_inval_due_to_ipv6_tentative_pref_src(self, conf_o)) {
-                    _LOG3D("route-sync: ignore failure to add IPv6 route with tentative IPv6 "
-                           "pref-src: %s: %s",
-                           nmp_object_to_string(conf_o,
-                                                NMP_OBJECT_TO_STRING_PUBLIC,
-                                                sbuf1,
-                                                sizeof(sbuf1)),
-                           nm_strerror(r));
-                    if (!*out_temporary_not_available)
-                        *out_temporary_not_available =
-                            g_ptr_array_new_full(0, (GDestroyNotify) nmp_object_unref);
-                    g_ptr_array_add(*out_temporary_not_available,
-                                    (gpointer) nmp_object_ref(conf_o));
-                } else if (!gateway_route_added
-                           && ((r == -ENETUNREACH && vt->is_ip4
-                                && !!NMP_OBJECT_CAST_IP4_ROUTE(conf_o)->gateway)
-                               || (r == -EHOSTUNREACH && !vt->is_ip4
-                                   && !IN6_IS_ADDR_UNSPECIFIED(
-                                       &NMP_OBJECT_CAST_IP6_ROUTE(conf_o)->gateway)))) {
-                    NMPObject oo;
-
-                    if (vt->is_ip4) {
-                        const NMPlatformIP4Route *rt = NMP_OBJECT_CAST_IP4_ROUTE(conf_o);
-
-                        nmp_object_stackinit(
-                            &oo,
-                            NMP_OBJECT_TYPE_IP4_ROUTE,
-                            &((NMPlatformIP4Route){
-                                .ifindex       = rt->ifindex,
-                                .network       = rt->gateway,
-                                .plen          = 32,
-                                .metric        = nm_platform_ip4_route_get_effective_metric(rt),
-                                .rt_source     = rt->rt_source,
-                                .table_coerced = nm_platform_ip_route_get_effective_table(
-                                    NM_PLATFORM_IP_ROUTE_CAST(rt)),
-                            }));
-                    } else {
-                        const NMPlatformIP6Route *rt = NMP_OBJECT_CAST_IP6_ROUTE(conf_o);
-
-                        nmp_object_stackinit(
-                            &oo,
-                            NMP_OBJECT_TYPE_IP6_ROUTE,
-                            &((NMPlatformIP6Route){
-                                .ifindex       = rt->ifindex,
-                                .network       = rt->gateway,
-                                .plen          = 128,
-                                .metric        = nm_platform_ip6_route_get_effective_metric(rt),
-                                .rt_source     = rt->rt_source,
-                                .table_coerced = nm_platform_ip_route_get_effective_table(
-                                    NM_PLATFORM_IP_ROUTE_CAST(rt)),
-                            }));
-                    }
-
-                    _LOG3D("route-sync: failure to add IPv%c route: %s: %s; try adding direct "
-                           "route to gateway %s",
-                           vt->is_ip4 ? '4' : '6',
-                           nmp_object_to_string(conf_o,
-                                                NMP_OBJECT_TO_STRING_PUBLIC,
-                                                sbuf1,
-                                                sizeof(sbuf1)),
-                           nm_strerror(r),
-                           nmp_object_to_string(&oo,
-                                                NMP_OBJECT_TO_STRING_PUBLIC,
-                                                sbuf2,
-                                                sizeof(sbuf2)));
-
-                    r2 = nm_platform_ip_route_add(self,
-                                                  NMP_NLM_FLAG_APPEND
-                                                      | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE,
-                                                  &oo);
-
-                    if (r2 < 0) {
-                        _LOG3D("route-sync: failure to add gateway IPv%c route: %s: %s",
-                               vt->is_ip4 ? '4' : '6',
-                               nmp_object_to_string(conf_o,
-                                                    NMP_OBJECT_TO_STRING_PUBLIC,
-                                                    sbuf1,
-                                                    sizeof(sbuf1)),
-                               nm_strerror(r2));
-                    }
-
-                    gateway_route_added = TRUE;
-                    goto sync_route_add;
-                } else {
-                    _LOG3W("route-sync: failure to add IPv%c route: %s: %s",
-                           vt->is_ip4 ? '4' : '6',
-                           nmp_object_to_string(conf_o,
-                                                NMP_OBJECT_TO_STRING_PUBLIC,
-                                                sbuf1,
-                                                sizeof(sbuf1)),
-                           nm_strerror(r));
-                    success = FALSE;
-                }
-            }
-        }
-    }
-
-    if (routes_prune) {
-        for (i = 0; i < routes_prune->len; i++) {
-            const NMPObject *prune_o;
-
-            prune_o = routes_prune->pdata[i];
-
-            nm_assert((NM_IS_IPv4(addr_family)
-                       && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP4_ROUTE)
-                      || (!NM_IS_IPv4(addr_family)
-                          && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP6_ROUTE));
-
-            if (routes_idx && g_hash_table_lookup(routes_idx, prune_o))
-                continue;
-
-            if (!nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, prune_o))
-                continue;
-
-            if (!nm_platform_object_delete(self, prune_o)) {
-                /* ignore error... */
-            }
-        }
-    }
-
-    return success;
-}
-
-gboolean
-nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex)
-{
-    gboolean success = TRUE;
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
-
-    if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) {
-        gs_unref_ptrarray GPtrArray *routes_prune = NULL;
-
-        routes_prune = nm_platform_ip_route_get_prune_list(self,
-                                                           AF_INET,
-                                                           ifindex,
-                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL);
-        success &= nm_platform_ip_route_sync(self, AF_INET, ifindex, NULL, routes_prune, NULL);
-    }
-    if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) {
-        gs_unref_ptrarray GPtrArray *routes_prune = NULL;
-
-        routes_prune = nm_platform_ip_route_get_prune_list(self,
-                                                           AF_INET6,
-                                                           ifindex,
-                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL);
-        success &= nm_platform_ip_route_sync(self, AF_INET6, ifindex, NULL, routes_prune, NULL);
-    }
-    return success;
-}
-
-/*****************************************************************************/
-
-static guint8
-_ip_route_scope_inv_get_normalized(const NMPlatformIP4Route *route)
-{
-    /* in kernel, you cannot set scope to RT_SCOPE_NOWHERE (255).
-     * That means, in NM, we treat RT_SCOPE_NOWHERE as unset, and detect
-     * it based on the presence of the gateway. In other words, when adding
-     * a route with scope RT_SCOPE_NOWHERE (in NetworkManager) to kernel,
-     * the resulting scope will be either "link" or "universe" (depending
-     * on the gateway).
-     *
-     * Note that internally, we track @scope_inv is the inverse of scope,
-     * so that the default equals zero (~(RT_SCOPE_NOWHERE)).
-     **/
-    if (route->scope_inv == 0) {
-        if (route->type_coerced == nm_platform_route_type_coerce(RTN_LOCAL))
-            return nm_platform_route_scope_inv(RT_SCOPE_HOST);
-        else {
-            return nm_platform_route_scope_inv(!route->gateway ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
-        }
-    }
-    return route->scope_inv;
-}
-
-static guint8
-_route_pref_normalize(guint8 pref)
-{
-    /* for kernel (and ICMPv6) pref can only have one of 3 values. Normalize. */
-    return NM_IN_SET(pref, NM_ICMPV6_ROUTER_PREF_LOW, NM_ICMPV6_ROUTER_PREF_HIGH)
-               ? pref
-               : NM_ICMPV6_ROUTER_PREF_MEDIUM;
-}
-
-/**
- * nm_platform_ip_route_normalize:
- * @addr_family: AF_INET or AF_INET6
- * @route: an NMPlatformIP4Route or NMPlatformIP6Route instance, depending on @addr_family.
- *
- * Adding a route to kernel via nm_platform_ip_route_add() will normalize/coerce some
- * properties of the route. This function modifies (normalizes) the route like it
- * would be done by adding the route in kernel.
- *
- * Note that this function is related to NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY
- * in that if two routes compare semantically equal, after normalizing they also shall
- * compare equal with NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL.
- */
-void
-nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route)
-{
-    NMPlatformIP4Route *r4;
-    NMPlatformIP6Route *r6;
-
-    route->table_coerced =
-        nm_platform_route_table_coerce(nm_platform_ip_route_get_effective_table(route));
-    route->table_any = FALSE;
-
-    route->rt_source = nmp_utils_ip_config_source_round_trip_rtprot(route->rt_source);
-
-    switch (addr_family) {
-    case AF_INET:
-        r4                = (NMPlatformIP4Route *) route;
-        route->metric     = nm_platform_ip4_route_get_effective_metric(r4);
-        route->metric_any = FALSE;
-        r4->network       = nm_utils_ip4_address_clear_host_address(r4->network, r4->plen);
-        r4->scope_inv     = _ip_route_scope_inv_get_normalized(r4);
-        break;
-    case AF_INET6:
-        r6                = (NMPlatformIP6Route *) route;
-        route->metric     = nm_platform_ip6_route_get_effective_metric(r6);
-        route->metric_any = FALSE;
-        nm_utils_ip6_address_clear_host_address(&r6->network, &r6->network, r6->plen);
-        nm_utils_ip6_address_clear_host_address(&r6->src, &r6->src, r6->src_plen);
-        break;
-    default:
-        nm_assert_not_reached();
-        break;
-    }
-}
-
-static int
-_ip_route_add(NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route)
-{
-    char sbuf[sizeof(_nm_utils_to_string_buffer)];
-    int  ifindex;
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    nm_assert(route);
-    nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
-
-    ifindex = ((const NMPlatformIPRoute *) route)->ifindex;
-    _LOG3D("route: %-10s IPv%c route: %s",
-           _nmp_nlm_flag_to_string(flags & NMP_NLM_FLAG_FMASK),
-           nm_utils_addr_family_to_char(addr_family),
-           NM_IS_IPv4(addr_family) ? nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf))
-                                   : nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf)));
-
-    return klass->ip_route_add(self, flags, addr_family, route);
-}
-
-int
-nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route)
-{
-    int addr_family;
-
-    switch (NMP_OBJECT_GET_TYPE(route)) {
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-        addr_family = AF_INET;
-        break;
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-        addr_family = AF_INET6;
-        break;
-    default:
-        g_return_val_if_reached(FALSE);
-    }
-
-    return _ip_route_add(self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE(route));
-}
-
-int
-nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route)
-{
-    return _ip_route_add(self, flags, AF_INET, route);
-}
-
-int
-nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route)
-{
-    return _ip_route_add(self, flags, AF_INET6, route);
-}
-
-gboolean
-nm_platform_object_delete(NMPlatform *self, const NMPObject *obj)
-{
-    int ifindex;
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    switch (NMP_OBJECT_GET_TYPE(obj)) {
-    case NMP_OBJECT_TYPE_ROUTING_RULE:
-        _LOGD("%s: delete %s",
-              NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
-              nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-        break;
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-    case NMP_OBJECT_TYPE_QDISC:
-    case NMP_OBJECT_TYPE_TFILTER:
-        ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex;
-        _LOG3D("%s: delete %s",
-               NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
-               nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-        break;
-    default:
-        g_return_val_if_reached(FALSE);
-    }
-
-    return klass->object_delete(self, obj);
-}
-
-/*****************************************************************************/
-
-int
-nm_platform_ip_route_get(NMPlatform *  self,
-                         int           addr_family,
-                         gconstpointer address /* in_addr_t or struct in6_addr */,
-                         int           oif_ifindex,
-                         NMPObject **  out_route)
-{
-    nm_auto_nmpobj NMPObject *route = NULL;
-    int                       result;
-    char                      buf[NM_UTILS_INET_ADDRSTRLEN];
-    char                      buf_oif[64];
-
-    _CHECK_SELF(self, klass, FALSE);
-
-    g_return_val_if_fail(address, -NME_BUG);
-    g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), -NME_BUG);
-
-    _LOGT("route: get IPv%c route for: %s%s",
-          nm_utils_addr_family_to_char(addr_family),
-          inet_ntop(addr_family, address, buf, sizeof(buf)),
-          oif_ifindex > 0 ? nm_sprintf_buf(buf_oif, " oif %d", oif_ifindex) : "");
-
-    if (!klass->ip_route_get)
-        result = -NME_PL_OPNOTSUPP;
-    else {
-        result = klass->ip_route_get(self, addr_family, address, oif_ifindex, &route);
-    }
-
-    if (result < 0) {
-        nm_assert(!route);
-        _LOGW("route: get IPv%c route for: %s failed with %s",
-              nm_utils_addr_family_to_char(addr_family),
-              inet_ntop(addr_family, address, buf, sizeof(buf)),
-              nm_strerror(result));
-    } else {
-        nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(route),
-                            NMP_OBJECT_TYPE_IP4_ROUTE,
-                            NMP_OBJECT_TYPE_IP6_ROUTE));
-        nm_assert(!NMP_OBJECT_IS_STACKINIT(route));
-        nm_assert(route->parent._ref_count == 1);
-        _LOGD("route: get IPv%c route for: %s succeeded: %s",
-              nm_utils_addr_family_to_char(addr_family),
-              inet_ntop(addr_family, address, buf, sizeof(buf)),
-              nmp_object_to_string(route, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-        NM_SET_OUT(out_route, g_steal_pointer(&route));
-    }
-    return result;
-}
-
-/*****************************************************************************/
-
-#define IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS ((int) 1500)
-#define IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S \
-    ((int) (((IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS + 999) * 3) / 1000))
-
-static gint64
-_ip4_dev_route_blacklist_timeout_ms_get(gint64 timeout_msec)
-{
-    return timeout_msec >> 1;
-}
-
-static gint64
-_ip4_dev_route_blacklist_timeout_ms_marked(gint64 timeout_msec)
-{
-    return !!(timeout_msec & ((gint64) 1));
-}
-
-static gboolean
-_ip4_dev_route_blacklist_check_cb(gpointer user_data)
-{
-    NMPlatform *       self = user_data;
-    NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
-    GHashTableIter     iter;
-    const NMPObject *  p_obj;
-    gint64 *           p_timeout_ms;
-    gint64             now_ms;
-
-    priv->ip4_dev_route_blacklist_check_id = 0;
-
-again:
-    if (!priv->ip4_dev_route_blacklist_hash)
-        goto out;
-
-    now_ms = nm_utils_get_monotonic_timestamp_msec();
-
-    g_hash_table_iter_init(&iter, priv->ip4_dev_route_blacklist_hash);
-    while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) {
-        if (!_ip4_dev_route_blacklist_timeout_ms_marked(*p_timeout_ms))
-            continue;
-
-        /* unmark because we checked it. */
-        *p_timeout_ms = *p_timeout_ms & ~((gint64) 1);
-
-        if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms))
-            continue;
-
-        if (!nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, p_obj))
-            continue;
-
-        _LOGT("ip4-dev-route: delete %s",
-              nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-        nm_platform_object_delete(self, p_obj);
-        goto again;
-    }
-
-out:
-    return G_SOURCE_REMOVE;
-}
-
-static void
-_ip4_dev_route_blacklist_check_schedule(NMPlatform *self)
-{
-    NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
-
-    if (!priv->ip4_dev_route_blacklist_check_id) {
-        priv->ip4_dev_route_blacklist_check_id =
-            g_idle_add_full(G_PRIORITY_HIGH, _ip4_dev_route_blacklist_check_cb, self, NULL);
-    }
-}
-
-static void
-_ip4_dev_route_blacklist_notify_route(NMPlatform *self, const NMPObject *obj)
-{
-    NMPlatformPrivate *priv;
-    const NMPObject *  p_obj;
-    gint64 *           p_timeout_ms;
-    gint64             now_ms;
-
-    nm_assert(NM_IS_PLATFORM(self));
-    nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE);
-
-    priv = NM_PLATFORM_GET_PRIVATE(self);
-
-    nm_assert(priv->ip4_dev_route_blacklist_gc_timeout_id);
-
-    if (!g_hash_table_lookup_extended(priv->ip4_dev_route_blacklist_hash,
-                                      obj,
-                                      (gpointer *) &p_obj,
-                                      (gpointer *) &p_timeout_ms))
-        return;
-
-    now_ms = nm_utils_get_monotonic_timestamp_msec();
-    if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms)) {
-        /* already expired. Wait for gc. */
-        return;
-    }
-
-    if (_ip4_dev_route_blacklist_timeout_ms_marked(*p_timeout_ms)) {
-        nm_assert(priv->ip4_dev_route_blacklist_check_id);
-        return;
-    }
-
-    /* We cannot delete it right away because we are in the process of receiving netlink messages.
-     * It may be possible to do so, but complicated and error prone.
-     *
-     * Instead, we mark the entry and schedule an idle action (with high priority). */
-    *p_timeout_ms = (*p_timeout_ms) | ((gint64) 1);
-    _ip4_dev_route_blacklist_check_schedule(self);
-}
-
-static gboolean
-_ip4_dev_route_blacklist_gc_timeout_handle(gpointer user_data)
-{
-    NMPlatform *       self = user_data;
-    NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
-    GHashTableIter     iter;
-    const NMPObject *  p_obj;
-    gint64 *           p_timeout_ms;
-    gint64             now_ms;
-
-    nm_assert(priv->ip4_dev_route_blacklist_gc_timeout_id);
-
-    now_ms = nm_utils_get_monotonic_timestamp_msec();
-
-    g_hash_table_iter_init(&iter, priv->ip4_dev_route_blacklist_hash);
-    while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) {
-        if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms)) {
-            _LOGT("ip4-dev-route: cleanup %s",
-                  nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-            g_hash_table_iter_remove(&iter);
-        }
-    }
-
-    _ip4_dev_route_blacklist_schedule(self);
-    return G_SOURCE_CONTINUE;
-}
-
-static void
-_ip4_dev_route_blacklist_schedule(NMPlatform *self)
-{
-    NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
-
-    if (!priv->ip4_dev_route_blacklist_hash
-        || g_hash_table_size(priv->ip4_dev_route_blacklist_hash) == 0) {
-        nm_clear_pointer(&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref);
-        nm_clear_g_source(&priv->ip4_dev_route_blacklist_gc_timeout_id);
-    } else {
-        if (!priv->ip4_dev_route_blacklist_gc_timeout_id) {
-            /* this timeout is only to garbage collect the expired entries from priv->ip4_dev_route_blacklist_hash.
-             * It can run infrequently, and it doesn't hurt if expired entries linger around a bit
-             * longer then necessary. */
-            priv->ip4_dev_route_blacklist_gc_timeout_id =
-                g_timeout_add_seconds(IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S,
-                                      _ip4_dev_route_blacklist_gc_timeout_handle,
-                                      self);
-        }
-    }
-}
-
-/**
- * nm_platform_ip4_dev_route_blacklist_set:
- * @self:
- * @ifindex:
- * @ip4_dev_route_blacklist:
- *
- * When adding an IP address, kernel automatically adds a device route.
- * This can be suppressed via the IFA_F_NOPREFIXROUTE address flag. For proper
- * IPv6 support, we require kernel support for IFA_F_NOPREFIXROUTE and always
- * add the device route manually.
- *
- * For IPv4, this flag is rather new and we don't rely on it yet. We want to use
- * it (but currently still don't). So, for IPv4, kernel possibly adds a device
- * route, however it has a wrong metric of zero. We add our own device route (with
- * proper metric), but need to delete the route that kernel adds.
- *
- * The problem is, that kernel does not immediately add the route, when adding
- * the address. It only shows up some time later. So, we register here a list
- * of blacklisted routes, and when they show up within a time out, we assume it's
- * the kernel generated one, and we delete it.
- *
- * Eventually, we want to get rid of this and use IFA_F_NOPREFIXROUTE for IPv4
- * routes as well.
- */
-void
-nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self,
-                                        int         ifindex,
-                                        GPtrArray * ip4_dev_route_blacklist)
-{
-    NMPlatformPrivate *priv;
-    GHashTableIter     iter;
-    const NMPObject *  p_obj;
-    guint              i;
-    gint64             timeout_msec;
-    gint64             timeout_msec_val;
-    gint64 *           p_timeout_ms;
-    gboolean           needs_check = FALSE;
-
-    nm_assert(NM_IS_PLATFORM(self));
-    nm_assert(ifindex > 0);
-
-    /* TODO: the blacklist should be maintained by NML3Cfg. */
-
-    priv = NM_PLATFORM_GET_PRIVATE(self);
-
-    /* first, expire all for current ifindex... */
-    if (priv->ip4_dev_route_blacklist_hash) {
-        g_hash_table_iter_init(&iter, priv->ip4_dev_route_blacklist_hash);
-        while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) {
-            if (NMP_OBJECT_CAST_IP4_ROUTE(p_obj)->ifindex == ifindex) {
-                /* we could g_hash_table_iter_remove(&iter) the current entry.
-                 * Instead, just expire it and let _ip4_dev_route_blacklist_gc_timeout_handle()
-                 * handle it.
-                 *
-                 * The assumption is, that ip4_dev_route_blacklist contains the very same entry
-                 * again, with a new timeout. So, we can un-expire it below. */
-                *p_timeout_ms = 0;
-            }
-        }
-    }
-
-    if (ip4_dev_route_blacklist && ip4_dev_route_blacklist->len > 0) {
-        if (!priv->ip4_dev_route_blacklist_hash) {
-            priv->ip4_dev_route_blacklist_hash =
-                g_hash_table_new_full((GHashFunc) nmp_object_id_hash,
-                                      (GEqualFunc) nmp_object_id_equal,
-                                      (GDestroyNotify) nmp_object_unref,
-                                      nm_g_slice_free_fcn_gint64);
-        }
-
-        timeout_msec = nm_utils_get_monotonic_timestamp_msec() + IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS;
-        timeout_msec_val = (timeout_msec << 1) | ((gint64) 1);
-        for (i = 0; i < ip4_dev_route_blacklist->len; i++) {
-            const NMPObject *o;
-
-            needs_check = TRUE;
-            o           = ip4_dev_route_blacklist->pdata[i];
-            if (g_hash_table_lookup_extended(priv->ip4_dev_route_blacklist_hash,
-                                             o,
-                                             (gpointer *) &p_obj,
-                                             (gpointer *) &p_timeout_ms)) {
-                if (nmp_object_equal(p_obj, o)) {
-                    /* un-expire and reuse the entry. */
-                    _LOGT("ip4-dev-route: register %s (update)",
-                          nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-                    *p_timeout_ms = timeout_msec_val;
-                    continue;
-                }
-            }
-
-            _LOGT("ip4-dev-route: register %s",
-                  nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-            p_timeout_ms  = g_slice_new(gint64);
-            *p_timeout_ms = timeout_msec_val;
-            g_hash_table_replace(priv->ip4_dev_route_blacklist_hash,
-                                 (gpointer) nmp_object_ref(o),
-                                 p_timeout_ms);
-        }
-    }
-
-    _ip4_dev_route_blacklist_schedule(self);
-
-    if (needs_check)
-        _ip4_dev_route_blacklist_check_schedule(self);
-}
-
-/*****************************************************************************/
-
-int
-nm_platform_routing_rule_add(NMPlatform *                 self,
-                             NMPNlmFlags                  flags,
-                             const NMPlatformRoutingRule *routing_rule)
-{
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    g_return_val_if_fail(routing_rule, -NME_BUG);
-
-    _LOGD("routing-rule: adding or updating: %s",
-          nm_platform_routing_rule_to_string(routing_rule, NULL, 0));
-    return klass->routing_rule_add(self, flags, routing_rule);
-}
-
-/*****************************************************************************/
-
-int
-nm_platform_qdisc_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc)
-{
-    int ifindex = qdisc->ifindex;
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind
-     * is undefined. */
-
-    _LOG3D("adding or updating a qdisc: %s", nm_platform_qdisc_to_string(qdisc, NULL, 0));
-    return klass->qdisc_add(self, flags, qdisc);
-}
-
-/**
- * nm_platform_qdisc_sync:
- * @self: the #NMPlatform instance
- * @ifindex: the ifindex where to configure the qdiscs.
- * @known_qdiscs: the list of qdiscs (#NMPObject).
- *
- * The function promises not to take any reference to the qdisc
- * instances from @known_qdiscs, nor to keep them around after
- * the function returns. This is important, because it allows the
- * caller to pass NMPlatformQdisc instances which "kind" string
- * have a limited lifetime.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-nm_platform_qdisc_sync(NMPlatform *self, int ifindex, GPtrArray *known_qdiscs)
-{
-    gs_unref_ptrarray GPtrArray *plat_qdiscs = NULL;
-    NMPLookup                    lookup;
-    guint                        i;
-    gboolean                     success            = TRUE;
-    gs_unref_hashtable GHashTable *known_qdiscs_idx = NULL;
-
-    nm_assert(NM_IS_PLATFORM(self));
-    nm_assert(ifindex > 0);
-
-    known_qdiscs_idx =
-        g_hash_table_new((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal);
-    if (known_qdiscs) {
-        for (i = 0; i < known_qdiscs->len; i++) {
-            const NMPObject *q = g_ptr_array_index(known_qdiscs, i);
-
-            if (!g_hash_table_insert(known_qdiscs_idx, (gpointer) q, (gpointer) q)) {
-                _LOGW("duplicate qdisc %s", nm_platform_qdisc_to_string(&q->qdisc, NULL, 0));
-                return FALSE;
-            }
-        }
-    }
-
-    plat_qdiscs =
-        nm_platform_lookup_clone(self,
-                                 nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_QDISC, ifindex),
-                                 NULL,
-                                 NULL);
-    if (plat_qdiscs) {
-        for (i = 0; i < plat_qdiscs->len; i++) {
-            const NMPObject *p = g_ptr_array_index(plat_qdiscs, i);
-            const NMPObject *k;
-
-            /* look up known qdisc with same parent */
-            k = g_hash_table_lookup(known_qdiscs_idx, p);
-
-            if (k) {
-                const NMPlatformQdisc *qdisc_k = NMP_OBJECT_CAST_QDISC(k);
-                const NMPlatformQdisc *qdisc_p = NMP_OBJECT_CAST_QDISC(p);
-
-                /* check other fields */
-                if (nm_platform_qdisc_cmp_full(qdisc_k, qdisc_p, FALSE) != 0
-                    || (qdisc_k->handle != qdisc_p->handle && qdisc_k != 0)) {
-                    k = NULL;
-                }
-            }
-
-            if (k) {
-                g_hash_table_remove(known_qdiscs_idx, k);
-            } else {
-                /* can't delete qdisc with zero handle */
-                if (TC_H_MAJ(p->qdisc.handle) != 0) {
-                    success &= nm_platform_object_delete(self, p);
-                }
-            }
-        }
-    }
-
-    if (known_qdiscs) {
-        for (i = 0; i < known_qdiscs->len; i++) {
-            const NMPObject *q = g_ptr_array_index(known_qdiscs, i);
-
-            if (g_hash_table_contains(known_qdiscs_idx, q)) {
-                success &=
-                    (nm_platform_qdisc_add(self, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_QDISC(q)) >= 0);
-            }
-        }
-    }
-
-    return success;
-}
-
-/*****************************************************************************/
-
-int
-nm_platform_tfilter_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter)
-{
-    int ifindex = tfilter->ifindex;
-    _CHECK_SELF(self, klass, -NME_BUG);
-
-    /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind
-     * and tfilter.action.kind is undefined. */
-
-    _LOG3D("adding or updating a tfilter: %s", nm_platform_tfilter_to_string(tfilter, NULL, 0));
-    return klass->tfilter_add(self, flags, tfilter);
-}
-
-/**
- * nm_platform_qdisc_sync:
- * @self: the #NMPlatform instance
- * @ifindex: the ifindex where to configure the qdiscs.
- * @known_tfilters: the list of tfilters (#NMPObject).
- *
- * The function promises not to take any reference to the tfilter
- * instances from @known_tfilters, nor to keep them around after
- * the function returns. This is important, because it allows the
- * caller to pass NMPlatformTfilter instances which "kind" string
- * have a limited lifetime.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-nm_platform_tfilter_sync(NMPlatform *self, int ifindex, GPtrArray *known_tfilters)
-{
-    gs_unref_ptrarray GPtrArray *plat_tfilters = NULL;
-    NMPLookup                    lookup;
-    guint                        i;
-    gboolean                     success              = TRUE;
-    gs_unref_hashtable GHashTable *known_tfilters_idx = NULL;
-
-    nm_assert(NM_IS_PLATFORM(self));
-    nm_assert(ifindex > 0);
-
-    known_tfilters_idx =
-        g_hash_table_new((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal);
-
-    if (known_tfilters) {
-        for (i = 0; i < known_tfilters->len; i++) {
-            const NMPObject *q = g_ptr_array_index(known_tfilters, i);
-
-            g_hash_table_insert(known_tfilters_idx, (gpointer) q, (gpointer) q);
-        }
-    }
-
-    plat_tfilters =
-        nm_platform_lookup_clone(self,
-                                 nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_TFILTER, ifindex),
-                                 NULL,
-                                 NULL);
-
-    if (plat_tfilters) {
-        for (i = 0; i < plat_tfilters->len; i++) {
-            const NMPObject *q = g_ptr_array_index(plat_tfilters, i);
-
-            if (!g_hash_table_lookup(known_tfilters_idx, q))
-                success &= nm_platform_object_delete(self, q);
-        }
-    }
-
-    if (known_tfilters) {
-        for (i = 0; i < known_tfilters->len; i++) {
-            const NMPObject *q = g_ptr_array_index(known_tfilters, i);
-
-            success &=
-                (nm_platform_tfilter_add(self, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_TFILTER(q)) >= 0);
-        }
-    }
-
-    return success;
-}
-
-/*****************************************************************************/
-
-const char *
-nm_platform_vlan_qos_mapping_to_string(const char *            name,
-                                       const NMVlanQosMapping *map,
-                                       gsize                   n_map,
-                                       char *                  buf,
-                                       gsize                   len)
-{
-    gsize i;
-    char *b;
-
-    nm_utils_to_string_buffer_init(&buf, &len);
-
-    if (!n_map) {
-        nm_utils_strbuf_append_str(&buf, &len, "");
-        return buf;
-    }
-
-    if (!map)
-        g_return_val_if_reached("");
-
-    b = buf;
-
-    if (name) {
-        nm_utils_strbuf_append_str(&b, &len, name);
-        nm_utils_strbuf_append_str(&b, &len, " {");
-    } else
-        nm_utils_strbuf_append_c(&b, &len, '{');
-
-    for (i = 0; i < n_map; i++)
-        nm_utils_strbuf_append(&b, &len, " %u:%u", map[i].from, map[i].to);
-    nm_utils_strbuf_append_str(&b, &len, " }");
-    return buf;
-}
-
-static const char *
-_lifetime_to_string(guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size)
-{
-    if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT)
-        return "forever";
-
-    g_snprintf(buf,
-               buf_size,
-               "%usec",
-               nm_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now));
-    return buf;
-}
-
-static const char *
-_lifetime_summary_to_string(gint32  now,
-                            guint32 timestamp,
-                            guint32 preferred,
-                            guint32 lifetime,
-                            char *  buf,
-                            size_t  buf_size)
-{
-    g_snprintf(buf,
-               buf_size,
-               " lifetime %d-%u[%u,%u]",
-               (signed) now,
-               (unsigned) timestamp,
-               (unsigned) preferred,
-               (unsigned) lifetime);
-    return buf;
-}
-
-/**
- * nm_platform_link_to_string:
- * @route: pointer to NMPlatformLink address structure
- * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
- * @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
- *
- * A method for converting an link struct into a string representation.
- *
- * Returns: a string representation of the link.
- */
-const char *
-nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
-{
-    char        master[20];
-    char        parent[20];
-    char        str_flags[1 + NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN + 1];
-    char        str_highlighted_flags[50];
-    char *      s;
-    gsize       l;
-    char        str_addrmode[30];
-    char        str_address[NM_UTILS_HWADDR_LEN_MAX * 3];
-    char        str_broadcast[NM_UTILS_HWADDR_LEN_MAX * 3];
-    char        str_inet6_token[NM_UTILS_INET_ADDRSTRLEN];
-    const char *str_link_type;
-
-    if (!nm_utils_to_string_buffer_init_null(link, &buf, &len))
-        return buf;
-
-    s = str_highlighted_flags;
-    l = sizeof(str_highlighted_flags);
-    if (NM_FLAGS_HAS(link->n_ifi_flags, IFF_NOARP))
-        nm_utils_strbuf_append_str(&s, &l, "NOARP,");
-    if (NM_FLAGS_HAS(link->n_ifi_flags, IFF_UP))
-        nm_utils_strbuf_append_str(&s, &l, "UP");
-    else
-        nm_utils_strbuf_append_str(&s, &l, "DOWN");
-    if (link->connected)
-        nm_utils_strbuf_append_str(&s, &l, ",LOWER_UP");
-    nm_assert(s > str_highlighted_flags && l > 0);
-
-    if (link->n_ifi_flags) {
-        str_flags[0] = ';';
-        nm_platform_link_flags2str(link->n_ifi_flags, &str_flags[1], sizeof(str_flags) - 1);
-    } else
-        str_flags[0] = '\0';
-
-    if (link->master)
-        g_snprintf(master, sizeof(master), " master %d", link->master);
-    else
-        master[0] = 0;
-
-    if (link->parent > 0)
-        g_snprintf(parent, sizeof(parent), "@%d", link->parent);
-    else if (link->parent == NM_PLATFORM_LINK_OTHER_NETNS)
-        g_strlcpy(parent, "@other-netns", sizeof(parent));
-    else
-        parent[0] = 0;
-
-    _nmp_link_address_to_string(&link->l_address, str_address);
-    _nmp_link_address_to_string(&link->l_broadcast, str_broadcast);
-
-    str_link_type = nm_link_type_to_string(link->type);
-
-    g_snprintf(
-        buf,
-        len,
-        "%d: "    /* ifindex */
-        "%s"      /* name */
-        "%s"      /* parent */
-        " <%s%s>" /* flags */
-        " mtu %d"
-        "%s"      /* master */
-        " arp %u" /* arptype */
-        " %s"     /* link->type */
-        "%s%s"    /* kind */
-        "%s"      /* is-in-udev */
-        "%s%s"    /* addr-gen-mode */
-        "%s%s"    /* l_address */
-        "%s%s"    /* l_broadcast */
-        "%s%s"    /* inet6_token */
-        "%s%s"    /* driver */
-        " rx:%" G_GUINT64_FORMAT ",%" G_GUINT64_FORMAT " tx:%" G_GUINT64_FORMAT
-        ",%" G_GUINT64_FORMAT,
-        link->ifindex,
-        link->name,
-        parent,
-        str_highlighted_flags,
-        str_flags,
-        link->mtu,
-        master,
-        link->arptype,
-        str_link_type ?: "???",
-        link->kind ? (g_strcmp0(str_link_type, link->kind) ? "/" : "*") : "?",
-        link->kind && g_strcmp0(str_link_type, link->kind) ? link->kind : "",
-        link->initialized ? " init" : " not-init",
-        link->inet6_addr_gen_mode_inv ? " addrgenmode " : "",
-        link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str(
-            _nm_platform_uint8_inv(link->inet6_addr_gen_mode_inv),
-            str_addrmode,
-            sizeof(str_addrmode))
-                                      : "",
-        str_address[0] ? " addr " : "",
-        str_address[0] ? str_address : "",
-        str_broadcast[0] ? " brd " : "",
-        str_broadcast[0] ? str_broadcast : "",
-        link->inet6_token.id ? " inet6token " : "",
-        link->inet6_token.id
-            ? nm_utils_inet6_interface_identifier_to_token(link->inet6_token, str_inet6_token)
-            : "",
-        link->driver ? " driver " : "",
-        link->driver ?: "",
-        link->rx_packets,
-        link->rx_bytes,
-        link->tx_packets,
-        link->tx_bytes);
-    return buf;
-}
-
-const NMPlatformLnkBridge nm_platform_lnk_bridge_default = {
-    .forward_delay                 = NM_BRIDGE_FORWARD_DELAY_DEF_SYS,
-    .hello_time                    = NM_BRIDGE_HELLO_TIME_DEF_SYS,
-    .max_age                       = NM_BRIDGE_MAX_AGE_DEF_SYS,
-    .ageing_time                   = NM_BRIDGE_AGEING_TIME_DEF_SYS,
-    .stp_state                     = FALSE,
-    .priority                      = NM_BRIDGE_PRIORITY_DEF,
-    .vlan_protocol                 = 0x8100,
-    .vlan_stats_enabled            = NM_BRIDGE_VLAN_STATS_ENABLED_DEF,
-    .group_fwd_mask                = 0,
-    .group_addr                    = NM_ETHER_ADDR_INIT(NM_BRIDGE_GROUP_ADDRESS_DEF_BIN),
-    .mcast_snooping                = NM_BRIDGE_MULTICAST_SNOOPING_DEF,
-    .mcast_router                  = 1,
-    .mcast_query_use_ifaddr        = NM_BRIDGE_MULTICAST_QUERY_USE_IFADDR_DEF,
-    .mcast_querier                 = NM_BRIDGE_MULTICAST_QUERIER_DEF,
-    .mcast_hash_max                = NM_BRIDGE_MULTICAST_HASH_MAX_DEF,
-    .mcast_last_member_count       = NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_DEF,
-    .mcast_startup_query_count     = NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_DEF,
-    .mcast_last_member_interval    = NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_DEF,
-    .mcast_membership_interval     = NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_DEF,
-    .mcast_querier_interval        = NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_DEF,
-    .mcast_query_interval          = NM_BRIDGE_MULTICAST_QUERY_INTERVAL_DEF,
-    .mcast_query_response_interval = NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_DEF,
-    .mcast_startup_query_interval  = NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_DEF,
-};
-
-const char *
-nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len)
-{
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    g_snprintf(buf,
-               len,
-               "forward_delay %u"
-               " hello_time %u"
-               " max_age %u"
-               " ageing_time %u"
-               " stp_state %d"
-               " priority %u"
-               " vlan_protocol %u"
-               " vlan_stats_enabled %d"
-               " group_fwd_mask %#x"
-               " group_address " NM_ETHER_ADDR_FORMAT_STR " mcast_snooping %d"
-               " mcast_router %u"
-               " mcast_query_use_ifaddr %d"
-               " mcast_querier %d"
-               " mcast_hash_max %u"
-               " mcast_last_member_count %u"
-               " mcast_startup_query_count %u"
-               " mcast_last_member_interval %" G_GUINT64_FORMAT
-               " mcast_membership_interval %" G_GUINT64_FORMAT
-               " mcast_querier_interval %" G_GUINT64_FORMAT
-               " mcast_query_interval %" G_GUINT64_FORMAT
-               " mcast_query_response_interval %" G_GUINT64_FORMAT
-               " mcast_startup_query_interval %" G_GUINT64_FORMAT "",
-               lnk->forward_delay,
-               lnk->hello_time,
-               lnk->max_age,
-               lnk->ageing_time,
-               (int) lnk->stp_state,
-               lnk->priority,
-               lnk->vlan_protocol,
-               (int) lnk->vlan_stats_enabled,
-               lnk->group_fwd_mask,
-               NM_ETHER_ADDR_FORMAT_VAL(&lnk->group_addr),
-               (int) lnk->mcast_snooping,
-               lnk->mcast_router,
-               (int) lnk->mcast_query_use_ifaddr,
-               (int) lnk->mcast_querier,
-               lnk->mcast_hash_max,
-               lnk->mcast_last_member_count,
-               lnk->mcast_startup_query_count,
-               lnk->mcast_last_member_interval,
-               lnk->mcast_membership_interval,
-               lnk->mcast_querier_interval,
-               lnk->mcast_query_interval,
-               lnk->mcast_query_response_interval,
-               lnk->mcast_startup_query_interval);
-    return buf;
-}
-
-const char *
-nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len)
-{
-    char str_local[30];
-    char str_local1[NM_UTILS_INET_ADDRSTRLEN];
-    char str_remote[30];
-    char str_remote1[NM_UTILS_INET_ADDRSTRLEN];
-    char str_ttl[30];
-    char str_tos[30];
-    char str_parent_ifindex[30];
-    char str_input_flags[30];
-    char str_output_flags[30];
-    char str_input_key[30];
-    char str_input_key1[NM_UTILS_INET_ADDRSTRLEN];
-    char str_output_key[30];
-    char str_output_key1[NM_UTILS_INET_ADDRSTRLEN];
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    g_snprintf(
-        buf,
-        len,
-        "gre%s" /* is_tap */
-        "%s"    /* remote */
-        "%s"    /* local */
-        "%s"    /* parent_ifindex */
-        "%s"    /* ttl */
-        "%s"    /* tos */
-        "%s"    /* path_mtu_discovery */
-        "%s"    /* iflags */
-        "%s"    /* oflags */
-        "%s"    /* ikey */
-        "%s"    /* okey */
-        "",
-        lnk->is_tap ? "tap" : "",
-        lnk->remote ? nm_sprintf_buf(str_remote,
-                                     " remote %s",
-                                     _nm_utils_inet4_ntop(lnk->remote, str_remote1))
-                    : "",
-        lnk->local
-            ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1))
-            : "",
-        lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex)
-                            : "",
-        lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
-        lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos))
-                 : "",
-        lnk->path_mtu_discovery ? "" : " nopmtudisc",
-        lnk->input_flags ? nm_sprintf_buf(str_input_flags, " iflags 0x%x", lnk->input_flags) : "",
-        lnk->output_flags ? nm_sprintf_buf(str_output_flags, " oflags 0x%x", lnk->output_flags)
-                          : "",
-        NM_FLAGS_HAS(lnk->input_flags, GRE_KEY) || lnk->input_key
-            ? nm_sprintf_buf(str_input_key,
-                             " ikey %s",
-                             _nm_utils_inet4_ntop(lnk->input_key, str_input_key1))
-            : "",
-        NM_FLAGS_HAS(lnk->output_flags, GRE_KEY) || lnk->output_key
-            ? nm_sprintf_buf(str_output_key,
-                             " okey %s",
-                             _nm_utils_inet4_ntop(lnk->output_key, str_output_key1))
-            : "");
-    return buf;
-}
-
-const char *
-nm_platform_lnk_infiniband_to_string(const NMPlatformLnkInfiniband *lnk, char *buf, gsize len)
-{
-    char str_p_key[64];
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    g_snprintf(buf,
-               len,
-               "infiniband"
-               "%s"   /* p_key */
-               "%s%s" /* mode */
-               "",
-               lnk->p_key ? nm_sprintf_buf(str_p_key, " pkey %d", lnk->p_key) : "",
-               lnk->mode ? " mode " : "",
-               lnk->mode ?: "");
-    return buf;
-}
-
-const char *
-nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len)
-{
-    char  str_local[30];
-    char  str_local1[NM_UTILS_INET_ADDRSTRLEN];
-    char  str_remote[30];
-    char  str_remote1[NM_UTILS_INET_ADDRSTRLEN];
-    char  str_ttl[30];
-    char  str_tclass[30];
-    char  str_flow[30];
-    char  str_encap[30];
-    char  str_proto[30];
-    char  str_parent_ifindex[30];
-    char *str_type;
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    if (lnk->is_gre)
-        str_type = lnk->is_tap ? "ip6gretap" : "ip6gre";
-    else
-        str_type = "ip6tnl";
-
-    g_snprintf(
-        buf,
-        len,
-        "%s" /* type */
-        "%s" /* remote */
-        "%s" /* local */
-        "%s" /* parent_ifindex */
-        "%s" /* ttl */
-        "%s" /* tclass */
-        "%s" /* encap limit */
-        "%s" /* flow label */
-        "%s" /* proto */
-        " flags 0x%x"
-        "",
-        str_type,
-        nm_sprintf_buf(str_remote, " remote %s", _nm_utils_inet6_ntop(&lnk->remote, str_remote1)),
-        nm_sprintf_buf(str_local, " local %s", _nm_utils_inet6_ntop(&lnk->local, str_local1)),
-        lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex)
-                            : "",
-        lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
-        lnk->tclass == 1 ? " tclass inherit"
-                         : nm_sprintf_buf(str_tclass, " tclass 0x%x", lnk->tclass),
-        nm_sprintf_buf(str_encap, " encap-limit %u", lnk->encap_limit),
-        nm_sprintf_buf(str_flow, " flow-label 0x05%x", lnk->flow_label),
-        nm_sprintf_buf(str_proto, " proto %u", lnk->proto),
-        (guint) lnk->flags);
-    return buf;
-}
-
-const char *
-nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize len)
-{
-    char str_local[30];
-    char str_local1[NM_UTILS_INET_ADDRSTRLEN];
-    char str_remote[30];
-    char str_remote1[NM_UTILS_INET_ADDRSTRLEN];
-    char str_ttl[30];
-    char str_tos[30];
-    char str_parent_ifindex[30];
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    g_snprintf(
-        buf,
-        len,
-        "ipip"
-        "%s" /* remote */
-        "%s" /* local */
-        "%s" /* parent_ifindex */
-        "%s" /* ttl */
-        "%s" /* tos */
-        "%s" /* path_mtu_discovery */
-        "",
-        lnk->remote ? nm_sprintf_buf(str_remote,
-                                     " remote %s",
-                                     _nm_utils_inet4_ntop(lnk->remote, str_remote1))
-                    : "",
-        lnk->local
-            ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1))
-            : "",
-        lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex)
-                            : "",
-        lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
-        lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos))
-                 : "",
-        lnk->path_mtu_discovery ? "" : " nopmtudisc");
-    return buf;
-}
-
-const char *
-nm_platform_lnk_macsec_to_string(const NMPlatformLnkMacsec *lnk, char *buf, gsize len)
-{
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    g_snprintf(buf,
-               len,
-               "macsec "
-               "sci %016llx "
-               "protect %s "
-               "cipher %016llx "
-               "icvlen %u "
-               "encodingsa %u "
-               "validate %u "
-               "encrypt %s "
-               "send_sci %s "
-               "end_station %s "
-               "scb %s "
-               "replay %s",
-               (unsigned long long) lnk->sci,
-               lnk->protect ? "on" : "off",
-               (unsigned long long) lnk->cipher_suite,
-               lnk->icv_length,
-               lnk->encoding_sa,
-               lnk->validation,
-               lnk->encrypt ? "on" : "off",
-               lnk->include_sci ? "on" : "off",
-               lnk->es ? "on" : "off",
-               lnk->scb ? "on" : "off",
-               lnk->replay_protect ? "on" : "off");
-    return buf;
-}
-
-const char *
-nm_platform_lnk_macvlan_to_string(const NMPlatformLnkMacvlan *lnk, char *buf, gsize len)
-{
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    g_snprintf(buf,
-               len,
-               "%s mode %u %s",
-               lnk->tap ? "macvtap" : "macvlan",
-               lnk->mode,
-               lnk->no_promisc ? "not-promisc" : "promisc");
-    return buf;
-}
-
-const char *
-nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len)
-{
-    char str_local[30];
-    char str_local1[NM_UTILS_INET_ADDRSTRLEN];
-    char str_remote[30];
-    char str_remote1[NM_UTILS_INET_ADDRSTRLEN];
-    char str_ttl[30];
-    char str_tos[30];
-    char str_flags[30];
-    char str_proto[30];
-    char str_parent_ifindex[30];
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    g_snprintf(
-        buf,
-        len,
-        "sit"
-        "%s" /* remote */
-        "%s" /* local */
-        "%s" /* parent_ifindex */
-        "%s" /* ttl */
-        "%s" /* tos */
-        "%s" /* path_mtu_discovery */
-        "%s" /* flags */
-        "%s" /* proto */
-        "",
-        lnk->remote ? nm_sprintf_buf(str_remote,
-                                     " remote %s",
-                                     _nm_utils_inet4_ntop(lnk->remote, str_remote1))
-                    : "",
-        lnk->local
-            ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1))
-            : "",
-        lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex)
-                            : "",
-        lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
-        lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos))
-                 : "",
-        lnk->path_mtu_discovery ? "" : " nopmtudisc",
-        lnk->flags ? nm_sprintf_buf(str_flags, " flags 0x%x", lnk->flags) : "",
-        lnk->proto ? nm_sprintf_buf(str_proto, " proto 0x%x", lnk->proto) : "");
-    return buf;
-}
-
-const char *
-nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len)
-{
-    char        str_owner[50];
-    char        str_group[50];
-    char        str_type[50];
-    const char *type;
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    if (lnk->type == IFF_TUN)
-        type = "tun";
-    else if (lnk->type == IFF_TAP)
-        type = "tap";
-    else
-        type = nm_sprintf_buf(str_type, "tun type %u", (guint) lnk->type);
-
-    g_snprintf(buf,
-               len,
-               "%s" /* type */
-               "%s" /* pi */
-               "%s" /* vnet_hdr */
-               "%s" /* multi_queue */
-               "%s" /* persist */
-               "%s" /* owner */
-               "%s" /* group */
-               "",
-               type,
-               lnk->pi ? " pi" : "",
-               lnk->vnet_hdr ? " vnet_hdr" : "",
-               lnk->multi_queue ? " multi_queue" : "",
-               lnk->persist ? " persist" : "",
-               lnk->owner_valid ? nm_sprintf_buf(str_owner, " owner %u", (guint) lnk->owner) : "",
-               lnk->group_valid ? nm_sprintf_buf(str_group, " group %u", (guint) lnk->group) : "");
-    return buf;
-}
-
-const char *
-nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len)
-{
-    char *b;
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    b = buf;
-
-    nm_utils_strbuf_append(&b, &len, "vlan %u", lnk->id);
-    if (lnk->flags)
-        nm_utils_strbuf_append(&b, &len, " flags 0x%x", lnk->flags);
-    return buf;
-}
-
-const char *
-nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len)
-{
-    char *b;
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    b = buf;
-
-    nm_utils_strbuf_append(&b, &len, "table %u", lnk->table);
-    return buf;
-}
-
-const char *
-nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len)
-{
-    char str_group[100];
-    char str_group6[100];
-    char str_local[100];
-    char str_local6[100];
-    char str_dev[25];
-    char str_limit[25];
-    char str_src_port[35];
-    char str_dst_port[25];
-    char str_tos[25];
-    char str_ttl[25];
-    char sbuf[NM_UTILS_INET_ADDRSTRLEN];
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    if (lnk->group == 0)
-        str_group[0] = '\0';
-    else {
-        g_snprintf(str_group,
-                   sizeof(str_group),
-                   " %s %s",
-                   IN_MULTICAST(ntohl(lnk->group)) ? "group" : "remote",
-                   _nm_utils_inet4_ntop(lnk->group, sbuf));
-    }
-    if (IN6_IS_ADDR_UNSPECIFIED(&lnk->group6))
-        str_group6[0] = '\0';
-    else {
-        g_snprintf(str_group6,
-                   sizeof(str_group6),
-                   " %s%s %s",
-                   IN6_IS_ADDR_MULTICAST(&lnk->group6) ? "group" : "remote",
-                   str_group[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */
-                   _nm_utils_inet6_ntop(&lnk->group6, sbuf));
-    }
-
-    if (lnk->local == 0)
-        str_local[0] = '\0';
-    else {
-        g_snprintf(str_local,
-                   sizeof(str_local),
-                   " local %s",
-                   _nm_utils_inet4_ntop(lnk->local, sbuf));
-    }
-    if (IN6_IS_ADDR_UNSPECIFIED(&lnk->local6))
-        str_local6[0] = '\0';
-    else {
-        g_snprintf(str_local6,
-                   sizeof(str_local6),
-                   " local%s %s",
-                   str_local[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */
-                   _nm_utils_inet6_ntop(&lnk->local6, sbuf));
-    }
-
-    g_snprintf(
-        buf,
-        len,
-        "vxlan"
-        " id %u"     /* id */
-        "%s%s"       /* group/group6 */
-        "%s%s"       /* local/local6 */
-        "%s"         /* dev */
-        "%s"         /* src_port_min/src_port_max */
-        "%s"         /* dst_port */
-        "%s"         /* learning */
-        "%s"         /* proxy */
-        "%s"         /* rsc */
-        "%s"         /* l2miss */
-        "%s"         /* l3miss */
-        "%s"         /* tos */
-        "%s"         /* ttl */
-        " ageing %u" /* ageing */
-        "%s"         /* limit */
-        "",
-        (guint) lnk->id,
-        str_group,
-        str_group6,
-        str_local,
-        str_local6,
-        lnk->parent_ifindex ? nm_sprintf_buf(str_dev, " dev %d", lnk->parent_ifindex) : "",
-        lnk->src_port_min || lnk->src_port_max
-            ? nm_sprintf_buf(str_src_port, " srcport %u %u", lnk->src_port_min, lnk->src_port_max)
-            : "",
-        lnk->dst_port ? nm_sprintf_buf(str_dst_port, " dstport %u", lnk->dst_port) : "",
-        !lnk->learning ? " nolearning" : "",
-        lnk->proxy ? " proxy" : "",
-        lnk->rsc ? " rsc" : "",
-        lnk->l2miss ? " l2miss" : "",
-        lnk->l3miss ? " l3miss" : "",
-        lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos %#x", lnk->tos),
-        lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : "",
-        lnk->ageing,
-        lnk->limit ? nm_sprintf_buf(str_limit, " maxaddr %u", lnk->limit) : "");
-    return buf;
-}
-
-const char *
-nm_platform_wireguard_peer_to_string(const NMPWireGuardPeer *peer, char *buf, gsize len)
-{
-    char *        buf0           = buf;
-    gs_free char *public_key_b64 = NULL;
-    char          s_sockaddr[NM_UTILS_INET_ADDRSTRLEN + 100];
-    char          s_endpoint[20 + sizeof(s_sockaddr)];
-    char          s_addr[NM_UTILS_INET_ADDRSTRLEN];
-    char          s_keepalive[100];
-    guint         i;
-
-    nm_utils_to_string_buffer_init(&buf, &len);
-
-    public_key_b64 = g_base64_encode(peer->public_key, sizeof(peer->public_key));
-
-    if (peer->endpoint.sa.sa_family != AF_UNSPEC) {
-        nm_sprintf_buf(
-            s_endpoint,
-            " endpoint %s",
-            nm_sock_addr_union_to_string(&peer->endpoint, s_sockaddr, sizeof(s_sockaddr)));
-    } else
-        s_endpoint[0] = '\0';
-
-    nm_utils_strbuf_append(
-        &buf,
-        &len,
-        "public-key %s"
-        "%s"                                                   /* preshared-key */
-        "%s"                                                   /* endpoint */
-        " rx %" G_GUINT64_FORMAT " tx %" G_GUINT64_FORMAT "%s" /* persistent-keepalive */
-        "%s",                                                  /* allowed-ips */
-        public_key_b64,
-        nm_utils_memeqzero_secret(peer->preshared_key, sizeof(peer->preshared_key))
-            ? ""
-            : " preshared-key (hidden)",
-        s_endpoint,
-        peer->rx_bytes,
-        peer->tx_bytes,
-        peer->persistent_keepalive_interval > 0
-            ? nm_sprintf_buf(s_keepalive,
-                             " keepalive %u",
-                             (guint) peer->persistent_keepalive_interval)
-            : "",
-        peer->allowed_ips_len > 0 ? " allowed-ips" : "");
-
-    for (i = 0; i < peer->allowed_ips_len; i++) {
-        const NMPWireGuardAllowedIP *allowed_ip = &peer->allowed_ips[i];
-
-        nm_utils_strbuf_append(&buf,
-                               &len,
-                               " %s/%u",
-                               nm_utils_inet_ntop(allowed_ip->family, &allowed_ip->addr, s_addr),
-                               allowed_ip->mask);
-    }
-
-    return buf0;
-}
-
-const char *
-nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len)
-{
-    gs_free char *public_b64 = NULL;
-
-    if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
-        return buf;
-
-    if (!nm_utils_memeqzero(lnk->public_key, sizeof(lnk->public_key)))
-        public_b64 = g_base64_encode(lnk->public_key, sizeof(lnk->public_key));
-
-    g_snprintf(buf,
-               len,
-               "wireguard"
-               "%s%s" /* public-key */
-               "%s"   /* private-key */
-               " listen-port %u"
-               " fwmark 0x%x",
-               public_b64 ? " public-key " : "",
-               public_b64 ?: "",
-               nm_utils_memeqzero_secret(lnk->private_key, sizeof(lnk->private_key))
-                   ? ""
-                   : " private-key (hidden)",
-               lnk->listen_port,
-               lnk->fwmark);
-
-    return buf;
-}
-
-/**
- * nm_platform_ip4_address_to_string:
- * @route: pointer to NMPlatformIP4Address address structure
- * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
- * @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
- *
- * A method for converting an address struct into a string representation.
- *
- * Example output: ""
- *
- * Returns: a string representation of the address.
- */
-const char *
-nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len)
-{
-    char        s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE];
-    char        s_address[INET_ADDRSTRLEN];
-    char        s_peer[INET_ADDRSTRLEN];
-    char        str_dev[TO_STRING_DEV_BUF_SIZE];
-    char        str_label[32];
-    char        str_lft[30], str_pref[30], str_time[50], s_source[50];
-    char *      str_peer = NULL;
-    const char *str_lft_p, *str_pref_p, *str_time_p;
-    gint32      now = nm_utils_get_monotonic_timestamp_sec();
-    in_addr_t   broadcast_address;
-    char        str_broadcast[INET_ADDRSTRLEN];
-
-    if (!nm_utils_to_string_buffer_init_null(address, &buf, &len))
-        return buf;
-
-    inet_ntop(AF_INET, &address->address, s_address, sizeof(s_address));
-
-    if (address->peer_address != address->address) {
-        inet_ntop(AF_INET, &address->peer_address, s_peer, sizeof(s_peer));
-        str_peer = g_strconcat(" ptp ", s_peer, NULL);
-    }
-
-    _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev));
-
-    if (*address->label)
-        g_snprintf(str_label, sizeof(str_label), " label %s", address->label);
-    else
-        str_label[0] = 0;
-
-    str_lft_p = _lifetime_to_string(address->timestamp,
-                                    address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT,
-                                    now,
-                                    str_lft,
-                                    sizeof(str_lft)),
-    str_pref_p =
-        (address->lifetime == address->preferred)
-            ? str_lft_p
-            : (_lifetime_to_string(address->timestamp,
-                                   address->lifetime ? MIN(address->preferred, address->lifetime)
-                                                     : NM_PLATFORM_LIFETIME_PERMANENT,
-                                   now,
-                                   str_pref,
-                                   sizeof(str_pref)));
-    str_time_p = _lifetime_summary_to_string(now,
-                                             address->timestamp,
-                                             address->preferred,
-                                             address->lifetime,
-                                             str_time,
-                                             sizeof(str_time));
-
-    broadcast_address = nm_platform_ip4_broadcast_address_from_addr(address);
-
-    g_snprintf(
-        buf,
-        len,
-        "%s/%d"
-        "%s%s" /* broadcast */
-        " lft %s"
-        " pref %s"
-        "%s" /* time */
-        "%s" /* peer  */
-        "%s" /* dev */
-        "%s" /* flags */
-        "%s" /* label */
-        " src %s"
-        "%s" /* external */
-        "%s" /* ip4acd_not_ready */
-        "",
-        s_address,
-        address->plen,
-        broadcast_address != 0u || address->use_ip4_broadcast_address
-            ? (address->use_ip4_broadcast_address ? " brd " : " brd* ")
-            : "",
-        broadcast_address != 0u || address->use_ip4_broadcast_address
-            ? _nm_utils_inet4_ntop(broadcast_address, str_broadcast)
-            : "",
-        str_lft_p,
-        str_pref_p,
-        str_time_p,
-        str_peer ?: "",
-        str_dev,
-        _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)),
-        str_label,
-        nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)),
-        address->external ? " ext" : "",
-        address->ip4acd_not_ready ? " ip4acd-not-ready" : "");
-    g_free(str_peer);
-    return buf;
-}
-
-NM_UTILS_FLAGS2STR_DEFINE(nm_platform_link_flags2str,
-                          unsigned,
-                          NM_UTILS_FLAGS2STR(IFF_LOOPBACK, "loopback"),
-                          NM_UTILS_FLAGS2STR(IFF_BROADCAST, "broadcast"),
-                          NM_UTILS_FLAGS2STR(IFF_POINTOPOINT, "pointopoint"),
-                          NM_UTILS_FLAGS2STR(IFF_MULTICAST, "multicast"),
-                          NM_UTILS_FLAGS2STR(IFF_NOARP, "noarp"),
-                          NM_UTILS_FLAGS2STR(IFF_ALLMULTI, "allmulti"),
-                          NM_UTILS_FLAGS2STR(IFF_PROMISC, "promisc"),
-                          NM_UTILS_FLAGS2STR(IFF_MASTER, "master"),
-                          NM_UTILS_FLAGS2STR(IFF_SLAVE, "slave"),
-                          NM_UTILS_FLAGS2STR(IFF_DEBUG, "debug"),
-                          NM_UTILS_FLAGS2STR(IFF_DYNAMIC, "dynamic"),
-                          NM_UTILS_FLAGS2STR(IFF_AUTOMEDIA, "automedia"),
-                          NM_UTILS_FLAGS2STR(IFF_PORTSEL, "portsel"),
-                          NM_UTILS_FLAGS2STR(IFF_NOTRAILERS, "notrailers"),
-                          NM_UTILS_FLAGS2STR(IFF_UP, "up"),
-                          NM_UTILS_FLAGS2STR(IFF_RUNNING, "running"),
-                          NM_UTILS_FLAGS2STR(IFF_LOWER_UP, "lowerup"),
-                          NM_UTILS_FLAGS2STR(IFF_DORMANT, "dormant"),
-                          NM_UTILS_FLAGS2STR(IFF_ECHO, "echo"), );
-
-NM_UTILS_ENUM2STR_DEFINE(nm_platform_link_inet6_addrgenmode2str,
-                         guint8,
-                         NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_NONE, "none"),
-                         NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"),
-                         NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"),
-                         NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_RANDOM, "random"), );
-
-NM_UTILS_FLAGS2STR_DEFINE(nm_platform_addr_flags2str,
-                          unsigned,
-                          NM_UTILS_FLAGS2STR(IFA_F_SECONDARY, "secondary"),
-                          NM_UTILS_FLAGS2STR(IFA_F_NODAD, "nodad"),
-                          NM_UTILS_FLAGS2STR(IFA_F_OPTIMISTIC, "optimistic"),
-                          NM_UTILS_FLAGS2STR(IFA_F_HOMEADDRESS, "homeaddress"),
-                          NM_UTILS_FLAGS2STR(IFA_F_DEPRECATED, "deprecated"),
-                          NM_UTILS_FLAGS2STR(IFA_F_PERMANENT, "permanent"),
-                          NM_UTILS_FLAGS2STR(IFA_F_MANAGETEMPADDR, "mngtmpaddr"),
-                          NM_UTILS_FLAGS2STR(IFA_F_NOPREFIXROUTE, "noprefixroute"),
-                          NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"), );
-
-NM_UTILS_ENUM2STR_DEFINE(nm_platform_route_scope2str,
-                         int,
-                         NM_UTILS_ENUM2STR(RT_SCOPE_NOWHERE, "nowhere"),
-                         NM_UTILS_ENUM2STR(RT_SCOPE_HOST, "host"),
-                         NM_UTILS_ENUM2STR(RT_SCOPE_LINK, "link"),
-                         NM_UTILS_ENUM2STR(RT_SCOPE_SITE, "site"),
-                         NM_UTILS_ENUM2STR(RT_SCOPE_UNIVERSE, "global"), );
-
-/**
- * nm_platform_ip6_address_to_string:
- * @route: pointer to NMPlatformIP6Address address structure
- * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
- * @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
- *
- * A method for converting an address struct into a string representation.
- *
- * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1"
- *
- * Returns: a string representation of the address.
- */
-const char *
-nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len)
-{
-    char        s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE];
-    char        s_address[INET6_ADDRSTRLEN];
-    char        s_peer[INET6_ADDRSTRLEN];
-    char        str_lft[30], str_pref[30], str_time[50], s_source[50];
-    char        str_dev[TO_STRING_DEV_BUF_SIZE];
-    char *      str_peer = NULL;
-    const char *str_lft_p, *str_pref_p, *str_time_p;
-    gint32      now = nm_utils_get_monotonic_timestamp_sec();
-
-    if (!nm_utils_to_string_buffer_init_null(address, &buf, &len))
-        return buf;
-
-    inet_ntop(AF_INET6, &address->address, s_address, sizeof(s_address));
-
-    if (!IN6_IS_ADDR_UNSPECIFIED(&address->peer_address)) {
-        inet_ntop(AF_INET6, &address->peer_address, s_peer, sizeof(s_peer));
-        str_peer = g_strconcat(" ptp ", s_peer, NULL);
-    }
-
-    _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev));
-
-    str_lft_p = _lifetime_to_string(address->timestamp,
-                                    address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT,
-                                    now,
-                                    str_lft,
-                                    sizeof(str_lft)),
-    str_pref_p =
-        (address->lifetime == address->preferred)
-            ? str_lft_p
-            : (_lifetime_to_string(address->timestamp,
-                                   address->lifetime ? MIN(address->preferred, address->lifetime)
-                                                     : NM_PLATFORM_LIFETIME_PERMANENT,
-                                   now,
-                                   str_pref,
-                                   sizeof(str_pref)));
-    str_time_p = _lifetime_summary_to_string(now,
-                                             address->timestamp,
-                                             address->preferred,
-                                             address->lifetime,
-                                             str_time,
-                                             sizeof(str_time));
-
-    g_snprintf(
-        buf,
-        len,
-        "%s/%d lft %s pref %s%s%s%s%s src %s%s",
-        s_address,
-        address->plen,
-        str_lft_p,
-        str_pref_p,
-        str_time_p,
-        str_peer ?: "",
-        str_dev,
-        _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)),
-        nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)),
-        address->external ? " ext" : "");
-    g_free(str_peer);
-    return buf;
-}
-
-static NM_UTILS_FLAGS2STR_DEFINE(_rtm_flags_to_string,
-                                 unsigned,
-                                 NM_UTILS_FLAGS2STR(RTNH_F_DEAD, "dead"),
-                                 NM_UTILS_FLAGS2STR(RTNH_F_PERVASIVE, "pervasive"),
-                                 NM_UTILS_FLAGS2STR(RTNH_F_ONLINK, "onlink"),
-                                 NM_UTILS_FLAGS2STR(8 /*RTNH_F_OFFLOAD*/, "offload"),
-                                 NM_UTILS_FLAGS2STR(16 /*RTNH_F_LINKDOWN*/, "linkdown"),
-                                 NM_UTILS_FLAGS2STR(32 /*RTNH_F_UNRESOLVED*/, "unresolved"),
-
-                                 NM_UTILS_FLAGS2STR(RTM_F_NOTIFY, "notify"),
-                                 NM_UTILS_FLAGS2STR(RTM_F_CLONED, "cloned"),
-                                 NM_UTILS_FLAGS2STR(RTM_F_EQUALIZE, "equalize"),
-                                 NM_UTILS_FLAGS2STR(RTM_F_PREFIX, "prefix"),
-                                 NM_UTILS_FLAGS2STR(0x1000 /*RTM_F_LOOKUP_TABLE*/, "lookup-table"),
-                                 NM_UTILS_FLAGS2STR(0x2000 /*RTM_F_FIB_MATCH*/, "fib-match"), );
-
-#define _RTM_FLAGS_TO_STRING_MAXLEN 200
-
-static const char *
-_rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags)
-{
-    const char *buf0 = buf;
-
-    nm_assert(buf_size >= _RTM_FLAGS_TO_STRING_MAXLEN);
-
-    if (!rtm_flags)
-        return "";
-
-    nm_utils_strbuf_append_str(&buf, &buf_size, " rtm_flags ");
-    _rtm_flags_to_string(rtm_flags, buf, buf_size);
-    nm_assert(strlen(buf) < buf_size);
-    return buf0;
-}
-
-/**
- * nm_platform_ip4_route_to_string:
- * @route: pointer to NMPlatformIP4Route route structure
- * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
- * @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
- *
- * A method for converting a route struct into a string representation.
- *
- * Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0"
- *
- * Returns: a string representation of the route.
- */
-const char *
-nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len)
-{
-    char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN];
-    char s_pref_src[INET_ADDRSTRLEN];
-    char str_dev[TO_STRING_DEV_BUF_SIZE];
-    char str_table[30];
-    char str_scope[30], s_source[50];
-    char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32];
-    char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN];
-    char str_type[30];
-    char str_metric[30];
-
-    if (!nm_utils_to_string_buffer_init_null(route, &buf, &len))
-        return buf;
-
-    inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network));
-    inet_ntop(AF_INET, &route->gateway, s_gateway, sizeof(s_gateway));
-
-    _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev));
-
-    g_snprintf(
-        buf,
-        len,
-        "type %s " /* type */
-        "%s"       /* table */
-        "%s/%d"
-        " via %s"
-        "%s"
-        " metric %s"
-        " mss %" G_GUINT32_FORMAT " rt-src %s" /* protocol */
-        "%s"                                   /* rtm_flags */
-        "%s%s"                                 /* scope */
-        "%s%s"                                 /* pref-src */
-        "%s"                                   /* tos */
-        "%s"                                   /* window */
-        "%s"                                   /* cwnd */
-        "%s"                                   /* initcwnd */
-        "%s"                                   /* initrwnd */
-        "%s"                                   /* mtu */
-        "",
-        nm_utils_route_type2str(nm_platform_route_type_uncoerce(route->type_coerced),
-                                str_type,
-                                sizeof(str_type)),
-        route->table_any
-            ? "table ?? "
-            : (route->table_coerced
-                   ? nm_sprintf_buf(str_table,
-                                    "table %u ",
-                                    nm_platform_route_table_uncoerce(route->table_coerced, FALSE))
-                   : ""),
-        s_network,
-        route->plen,
-        s_gateway,
-        str_dev,
-        route->metric_any
-            ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??")
-            : nm_sprintf_buf(str_metric, "%u", route->metric),
-        route->mss,
-        nmp_utils_ip_config_source_to_string(route->rt_source, s_source, sizeof(s_source)),
-        _rtm_flags_to_string_full(str_rtm_flags, sizeof(str_rtm_flags), route->r_rtm_flags),
-        route->scope_inv ? " scope " : "",
-        route->scope_inv
-            ? (nm_platform_route_scope2str(nm_platform_route_scope_inv(route->scope_inv),
-                                           str_scope,
-                                           sizeof(str_scope)))
-            : "",
-        route->pref_src ? " pref-src " : "",
-        route->pref_src ? inet_ntop(AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "",
-        route->tos ? nm_sprintf_buf(str_tos, " tos 0x%x", (unsigned) route->tos) : "",
-        route->window || route->lock_window ? nm_sprintf_buf(str_window,
-                                                             " window %s%" G_GUINT32_FORMAT,
-                                                             route->lock_window ? "lock " : "",
-                                                             route->window)
-                                            : "",
-        route->cwnd || route->lock_cwnd ? nm_sprintf_buf(str_cwnd,
-                                                         " cwnd %s%" G_GUINT32_FORMAT,
-                                                         route->lock_cwnd ? "lock " : "",
-                                                         route->cwnd)
-                                        : "",
-        route->initcwnd || route->lock_initcwnd
-            ? nm_sprintf_buf(str_initcwnd,
-                             " initcwnd %s%" G_GUINT32_FORMAT,
-                             route->lock_initcwnd ? "lock " : "",
-                             route->initcwnd)
-            : "",
-        route->initrwnd || route->lock_initrwnd
-            ? nm_sprintf_buf(str_initrwnd,
-                             " initrwnd %s%" G_GUINT32_FORMAT,
-                             route->lock_initrwnd ? "lock " : "",
-                             route->initrwnd)
-            : "",
-        route->mtu || route->lock_mtu ? nm_sprintf_buf(str_mtu,
-                                                       " mtu %s%" G_GUINT32_FORMAT,
-                                                       route->lock_mtu ? "lock " : "",
-                                                       route->mtu)
-                                      : "");
-    return buf;
-}
-
-/**
- * nm_platform_ip6_route_to_string:
- * @route: pointer to NMPlatformIP6Route route structure
- * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
- * @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
- *
- * A method for converting a route struct into a string representation.
- *
- * Example output: "ff02::fb/128 via :: dev em1 metric 0"
- *
- * Returns: a string representation of the route.
- */
-const char *
-nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len)
-{
-    char s_network[INET6_ADDRSTRLEN];
-    char s_gateway[INET6_ADDRSTRLEN];
-    char s_pref_src[INET6_ADDRSTRLEN];
-    char s_src_all[INET6_ADDRSTRLEN + 40];
-    char s_src[INET6_ADDRSTRLEN];
-    char str_type[30];
-    char str_table[30];
-    char str_pref[40];
-    char str_pref2[30];
-    char str_dev[TO_STRING_DEV_BUF_SIZE];
-    char s_source[50];
-    char str_window[32];
-    char str_cwnd[32];
-    char str_initcwnd[32];
-    char str_initrwnd[32];
-    char str_mtu[32];
-    char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN];
-    char str_metric[30];
-
-    if (!nm_utils_to_string_buffer_init_null(route, &buf, &len))
-        return buf;
-
-    inet_ntop(AF_INET6, &route->network, s_network, sizeof(s_network));
-    inet_ntop(AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway));
-
-    if (IN6_IS_ADDR_UNSPECIFIED(&route->pref_src))
-        s_pref_src[0] = 0;
-    else
-        inet_ntop(AF_INET6, &route->pref_src, s_pref_src, sizeof(s_pref_src));
-
-    _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev));
-
-    g_snprintf(
-        buf,
-        len,
-        "type %s " /* type */
-        "%s"       /* table */
-        "%s/%d"
-        " via %s"
-        "%s"
-        " metric %s"
-        " mss %" G_GUINT32_FORMAT " rt-src %s" /* protocol */
-        "%s"                                   /* source */
-        "%s"                                   /* rtm_flags */
-        "%s%s"                                 /* pref-src */
-        "%s"                                   /* window */
-        "%s"                                   /* cwnd */
-        "%s"                                   /* initcwnd */
-        "%s"                                   /* initrwnd */
-        "%s"                                   /* mtu */
-        "%s"                                   /* pref */
-        "",
-        nm_utils_route_type2str(nm_platform_route_type_uncoerce(route->type_coerced),
-                                str_type,
-                                sizeof(str_type)),
-        route->table_any
-            ? "table ?? "
-            : (route->table_coerced
-                   ? nm_sprintf_buf(str_table,
-                                    "table %u ",
-                                    nm_platform_route_table_uncoerce(route->table_coerced, FALSE))
-                   : ""),
-        s_network,
-        route->plen,
-        s_gateway,
-        str_dev,
-        route->metric_any
-            ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??")
-            : nm_sprintf_buf(str_metric, "%u", route->metric),
-        route->mss,
-        nmp_utils_ip_config_source_to_string(route->rt_source, s_source, sizeof(s_source)),
-        route->src_plen || !IN6_IS_ADDR_UNSPECIFIED(&route->src)
-            ? nm_sprintf_buf(s_src_all,
-                             " src %s/%u",
-                             _nm_utils_inet6_ntop(&route->src, s_src),
-                             (unsigned) route->src_plen)
-            : "",
-        _rtm_flags_to_string_full(str_rtm_flags, sizeof(str_rtm_flags), route->r_rtm_flags),
-        s_pref_src[0] ? " pref-src " : "",
-        s_pref_src[0] ? s_pref_src : "",
-        route->window || route->lock_window ? nm_sprintf_buf(str_window,
-                                                             " window %s%" G_GUINT32_FORMAT,
-                                                             route->lock_window ? "lock " : "",
-                                                             route->window)
-                                            : "",
-        route->cwnd || route->lock_cwnd ? nm_sprintf_buf(str_cwnd,
-                                                         " cwnd %s%" G_GUINT32_FORMAT,
-                                                         route->lock_cwnd ? "lock " : "",
-                                                         route->cwnd)
-                                        : "",
-        route->initcwnd || route->lock_initcwnd
-            ? nm_sprintf_buf(str_initcwnd,
-                             " initcwnd %s%" G_GUINT32_FORMAT,
-                             route->lock_initcwnd ? "lock " : "",
-                             route->initcwnd)
-            : "",
-        route->initrwnd || route->lock_initrwnd
-            ? nm_sprintf_buf(str_initrwnd,
-                             " initrwnd %s%" G_GUINT32_FORMAT,
-                             route->lock_initrwnd ? "lock " : "",
-                             route->initrwnd)
-            : "",
-        route->mtu || route->lock_mtu ? nm_sprintf_buf(str_mtu,
-                                                       " mtu %s%" G_GUINT32_FORMAT,
-                                                       route->lock_mtu ? "lock " : "",
-                                                       route->mtu)
-                                      : "",
-        route->rt_pref ? nm_sprintf_buf(
-            str_pref,
-            " pref %s",
-            nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2)))
-                       : "");
-
-    return buf;
-}
-
-static void
-_routing_rule_addr_to_string(char **         buf,
-                             gsize *         len,
-                             int             addr_family,
-                             const NMIPAddr *addr,
-                             guint8          plen,
-                             gboolean        is_src)
-{
-    char     s_addr[NM_UTILS_INET_ADDRSTRLEN];
-    gboolean is_zero;
-    gsize    addr_size;
-
-    nm_assert_addr_family(addr_family);
-    nm_assert(addr);
-
-    addr_size = nm_utils_addr_family_to_size(addr_family);
-
-    is_zero = nm_utils_memeqzero(addr, addr_size);
-
-    if (plen == 0 && is_zero) {
-        if (is_src)
-            nm_utils_strbuf_append_str(buf, len, " from all");
-        else
-            nm_utils_strbuf_append_str(buf, len, "");
-        return;
-    }
-
-    nm_utils_strbuf_append_str(buf, len, is_src ? " from " : " to ");
-
-    nm_utils_strbuf_append_str(buf, len, nm_utils_inet_ntop(addr_family, addr, s_addr));
-
-    if (plen != (addr_size * 8))
-        nm_utils_strbuf_append(buf, len, "/%u", plen);
-}
-
-static void
-_routing_rule_port_range_to_string(char **                   buf,
-                                   gsize *                   len,
-                                   const NMFibRulePortRange *port_range,
-                                   const char *              name)
-{
-    if (port_range->start == 0 && port_range->end == 0)
-        nm_utils_strbuf_append_str(buf, len, "");
-    else {
-        nm_utils_strbuf_append(buf, len, " %s %u", name, port_range->start);
-        if (port_range->start != port_range->end)
-            nm_utils_strbuf_append(buf, len, "-%u", port_range->end);
-    }
-}
-
-const char *
-nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len)
-{
-    const char *buf0;
-    guint32     rr_flags;
-
-    if (!nm_utils_to_string_buffer_init_null(routing_rule, &buf, &len))
-        return buf;
-
-    if (!NM_IN_SET(routing_rule->addr_family, AF_INET, AF_INET6)) {
-        /* invalid addr-family. The other fields are undefined. */
-        if (routing_rule->addr_family == AF_UNSPEC)
-            g_snprintf(buf, len, "[routing-rule]");
-        else
-            g_snprintf(buf, len, "[routing-rule family:%u]", routing_rule->addr_family);
-        return buf;
-    }
-
-    buf0 = buf;
-
-    rr_flags = routing_rule->flags;
-
-    rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_INVERT);
-    nm_utils_strbuf_append(&buf,
-                           &len,
-                           "[%c] " /* addr-family */
-                           "%u:"   /* priority */
-                           "%s",   /* not/FIB_RULE_INVERT */
-                           nm_utils_addr_family_to_char(routing_rule->addr_family),
-                           routing_rule->priority,
-                           (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_INVERT) ? " not" : ""));
-
-    _routing_rule_addr_to_string(&buf,
-                                 &len,
-                                 routing_rule->addr_family,
-                                 &routing_rule->src,
-                                 routing_rule->src_len,
-                                 TRUE);
-
-    _routing_rule_addr_to_string(&buf,
-                                 &len,
-                                 routing_rule->addr_family,
-                                 &routing_rule->dst,
-                                 routing_rule->dst_len,
-                                 FALSE);
-
-    if (routing_rule->tos)
-        nm_utils_strbuf_append(&buf, &len, " tos 0x%02x", routing_rule->tos);
-
-    if (routing_rule->fwmark != 0 || routing_rule->fwmask != 0) {
-        nm_utils_strbuf_append(&buf, &len, " fwmark %#x", (unsigned) routing_rule->fwmark);
-        if (routing_rule->fwmark != 0xFFFFFFFFu)
-            nm_utils_strbuf_append(&buf, &len, "/%#x", (unsigned) routing_rule->fwmask);
-    }
-
-    if (routing_rule->iifname[0]) {
-        nm_utils_strbuf_append(&buf, &len, " iif %s", routing_rule->iifname);
-        rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_IIF_DETACHED);
-        if (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_IIF_DETACHED))
-            nm_utils_strbuf_append_str(&buf, &len, " [detached]");
-    }
-
-    if (routing_rule->oifname[0]) {
-        nm_utils_strbuf_append(&buf, &len, " oif %s", routing_rule->oifname);
-        rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_OIF_DETACHED);
-        if (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_OIF_DETACHED))
-            nm_utils_strbuf_append_str(&buf, &len, " [detached]");
-    }
-
-    if (routing_rule->l3mdev != 0) {
-        if (routing_rule->l3mdev == 1)
-            nm_utils_strbuf_append_str(&buf, &len, " lookup [l3mdev-table]");
-        else {
-            nm_utils_strbuf_append(&buf,
-                                   &len,
-                                   " lookup [l3mdev-table/%u]",
-                                   (unsigned) routing_rule->l3mdev);
-        }
-    }
-
-    if (routing_rule->uid_range_has || routing_rule->uid_range.start
-        || routing_rule->uid_range.end) {
-        nm_utils_strbuf_append(&buf,
-                               &len,
-                               " uidrange %u-%u%s",
-                               routing_rule->uid_range.start,
-                               routing_rule->uid_range.end,
-                               routing_rule->uid_range_has ? "" : "(?)");
-    }
-
-    if (routing_rule->ip_proto != 0) {
-        /* we don't call getprotobynumber(), just print the numeric value.
-         * This differs from what ip-rule prints. */
-        nm_utils_strbuf_append(&buf, &len, " ipproto %u", routing_rule->ip_proto);
-    }
-
-    _routing_rule_port_range_to_string(&buf, &len, &routing_rule->sport_range, "sport");
-
-    _routing_rule_port_range_to_string(&buf, &len, &routing_rule->dport_range, "dport");
-
-    if (routing_rule->tun_id != 0) {
-        nm_utils_strbuf_append(&buf, &len, " tun_id %" G_GUINT64_FORMAT, routing_rule->tun_id);
-    }
-
-    if (routing_rule->table != 0) {
-        nm_utils_strbuf_append(&buf, &len, " lookup %u", routing_rule->table);
-    }
-
-    if (routing_rule->suppress_prefixlen_inverse != 0) {
-        nm_utils_strbuf_append(&buf,
-                               &len,
-                               " suppress_prefixlen %d",
-                               (int) (~routing_rule->suppress_prefixlen_inverse));
-    }
-
-    if (routing_rule->suppress_ifgroup_inverse != 0) {
-        nm_utils_strbuf_append(&buf,
-                               &len,
-                               " suppress_ifgroup %d",
-                               (int) (~routing_rule->suppress_ifgroup_inverse));
-    }
-
-    if (routing_rule->flow) {
-        /* FRA_FLOW is only for IPv4, but we want to print the value for all address-families,
-         * to see when it is set. In practice, this should not be set except for IPv4.
-         *
-         * We don't follow the style how ip-rule prints flow/realms. It's confusing. Just
-         * print the value hex. */
-        nm_utils_strbuf_append(&buf, &len, " realms 0x%08x", routing_rule->flow);
-    }
-
-    if (routing_rule->action == RTN_NAT) {
-        G_STATIC_ASSERT_EXPR(RTN_NAT == 10);
-
-        /* NAT is deprecated for many years. We don't support RTA_GATEWAY/FRA_UNUSED2
-         * for the gateway, and so do recent kernels ignore that parameter. */
-        nm_utils_strbuf_append_str(&buf, &len, " masquerade");
-    } else if (routing_rule->action == FR_ACT_GOTO) {
-        if (routing_rule->goto_target != 0)
-            nm_utils_strbuf_append(&buf, &len, " goto %u", routing_rule->goto_target);
-        else
-            nm_utils_strbuf_append_str(&buf, &len, " goto none");
-        rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_UNRESOLVED);
-        if (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_UNRESOLVED))
-            nm_utils_strbuf_append_str(&buf, &len, " unresolved");
-    } else if (routing_rule->action != FR_ACT_TO_TBL) {
-        const char *ss;
-        char        ss_buf[60];
-
-#define _V(v1, v2) ((sizeof(char[(((int) (v1)) == ((int) (v2))) ? 1 : -1]) * 0) + (v1))
-        switch (routing_rule->action) {
-        case _V(FR_ACT_UNSPEC, RTN_UNSPEC):
-            ss = "none";
-            break;
-        case _V(FR_ACT_TO_TBL, RTN_UNICAST):
-            ss = "unicast";
-            break;
-        case _V(FR_ACT_GOTO, RTN_LOCAL):
-            ss = "local";
-            break;
-        case _V(FR_ACT_NOP, RTN_BROADCAST):
-            ss = "nop";
-            break;
-        case _V(FR_ACT_RES3, RTN_ANYCAST):
-            ss = "anycast";
-            break;
-        case _V(FR_ACT_RES4, RTN_MULTICAST):
-            ss = "multicast";
-            break;
-        case _V(FR_ACT_BLACKHOLE, RTN_BLACKHOLE):
-            ss = "blackhole";
-            break;
-        case _V(FR_ACT_UNREACHABLE, RTN_UNREACHABLE):
-            ss = "unreachable";
-            break;
-        case _V(FR_ACT_PROHIBIT, RTN_PROHIBIT):
-            ss = "prohibit";
-            break;
-        case RTN_THROW:
-            ss = "throw";
-            break;
-        case RTN_NAT:
-            ss = "nat";
-            break;
-        case RTN_XRESOLVE:
-            ss = "xresolve";
-            break;
-        default:
-            ss = nm_sprintf_buf(ss_buf, "action-%u", routing_rule->action);
-            break;
-        }
-#undef _V
-        nm_utils_strbuf_append(&buf, &len, " %s", ss);
-    }
-
-    if (routing_rule->protocol != RTPROT_UNSPEC)
-        nm_utils_strbuf_append(&buf, &len, " protocol %u", routing_rule->protocol);
-
-    if (routing_rule->goto_target != 0 && routing_rule->action != FR_ACT_GOTO) {
-        /* a trailing target is set for an unexpected action. Print it. */
-        nm_utils_strbuf_append(&buf, &len, " goto-target %u", routing_rule->goto_target);
-    }
-
-    if (rr_flags != 0) {
-        /* we have some flags we didn't print about yet. */
-        nm_utils_strbuf_append(&buf, &len, " remaining-flags %x", rr_flags);
-    }
-
-    return buf0;
-}
-
-const char *
-nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len)
-{
-    char        str_dev[TO_STRING_DEV_BUF_SIZE];
-    const char *buf0;
-
-    if (!nm_utils_to_string_buffer_init_null(qdisc, &buf, &len))
-        return buf;
-
-    buf0 = buf;
-
-    nm_utils_strbuf_append(&buf,
-                           &len,
-                           "%s%s family %u handle %x parent %x info %x",
-                           qdisc->kind,
-                           _to_string_dev(NULL, qdisc->ifindex, str_dev, sizeof(str_dev)),
-                           qdisc->addr_family,
-                           qdisc->handle,
-                           qdisc->parent,
-                           qdisc->info);
-
-    if (nm_streq0(qdisc->kind, "fq_codel")) {
-        if (qdisc->fq_codel.limit)
-            nm_utils_strbuf_append(&buf, &len, " limit %u", qdisc->fq_codel.limit);
-        if (qdisc->fq_codel.flows)
-            nm_utils_strbuf_append(&buf, &len, " flows %u", qdisc->fq_codel.flows);
-        if (qdisc->fq_codel.target)
-            nm_utils_strbuf_append(&buf, &len, " target %u", qdisc->fq_codel.target);
-        if (qdisc->fq_codel.interval)
-            nm_utils_strbuf_append(&buf, &len, " interval %u", qdisc->fq_codel.interval);
-        if (qdisc->fq_codel.quantum)
-            nm_utils_strbuf_append(&buf, &len, " quantum %u", qdisc->fq_codel.quantum);
-        if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED)
-            nm_utils_strbuf_append(&buf, &len, " ce_threshold %u", qdisc->fq_codel.ce_threshold);
-        if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET)
-            nm_utils_strbuf_append(&buf, &len, " memory_limit %u", qdisc->fq_codel.memory_limit);
-        if (qdisc->fq_codel.ecn)
-            nm_utils_strbuf_append(&buf, &len, " ecn");
-    } else if (nm_streq0(qdisc->kind, "sfq")) {
-        if (qdisc->sfq.quantum)
-            nm_utils_strbuf_append(&buf, &len, " quantum %u", qdisc->sfq.quantum);
-        if (qdisc->sfq.perturb_period)
-            nm_utils_strbuf_append(&buf, &len, " perturb %d", qdisc->sfq.perturb_period);
-        if (qdisc->sfq.limit)
-            nm_utils_strbuf_append(&buf, &len, " limit %u", (guint) qdisc->sfq.limit);
-        if (qdisc->sfq.divisor)
-            nm_utils_strbuf_append(&buf, &len, " divisor %u", qdisc->sfq.divisor);
-        if (qdisc->sfq.flows)
-            nm_utils_strbuf_append(&buf, &len, " flows %u", qdisc->sfq.flows);
-        if (qdisc->sfq.depth)
-            nm_utils_strbuf_append(&buf, &len, " depth %u", qdisc->sfq.depth);
-    } else if (nm_streq0(qdisc->kind, "tbf")) {
-        nm_utils_strbuf_append(&buf, &len, " rate %" G_GUINT64_FORMAT, qdisc->tbf.rate);
-        nm_utils_strbuf_append(&buf, &len, " burst %u", qdisc->tbf.burst);
-        if (qdisc->tbf.limit)
-            nm_utils_strbuf_append(&buf, &len, " limit %u", qdisc->tbf.limit);
-        if (qdisc->tbf.latency)
-            nm_utils_strbuf_append(&buf, &len, " latency %uns", qdisc->tbf.latency);
-    }
-
-    return buf0;
-}
-
-void
-nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h)
-{
-    nm_hash_update_str0(h, obj->kind);
-    nm_hash_update_vals(h, obj->ifindex, obj->addr_family, obj->handle, obj->parent, obj->info);
-    if (nm_streq0(obj->kind, "fq_codel")) {
-        nm_hash_update_vals(h,
-                            obj->fq_codel.limit,
-                            obj->fq_codel.flows,
-                            obj->fq_codel.target,
-                            obj->fq_codel.interval,
-                            obj->fq_codel.quantum,
-                            obj->fq_codel.ce_threshold,
-                            obj->fq_codel.memory_limit,
-                            NM_HASH_COMBINE_BOOLS(guint8, obj->fq_codel.ecn));
-    } else if (nm_streq0(obj->kind, "sfq")) {
-        nm_hash_update_vals(h,
-                            obj->sfq.quantum,
-                            obj->sfq.perturb_period,
-                            obj->sfq.limit,
-                            obj->sfq.divisor,
-                            obj->sfq.flows,
-                            obj->sfq.depth);
-    } else if (nm_streq0(obj->kind, "tbf")) {
-        nm_hash_update_vals(h, obj->tbf.rate, obj->tbf.burst, obj->tbf.limit, obj->tbf.latency);
-    }
-}
-
-int
-nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a,
-                           const NMPlatformQdisc *b,
-                           gboolean               compare_handle)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, ifindex);
-    NM_CMP_FIELD(a, b, parent);
-    NM_CMP_FIELD_STR_INTERNED(a, b, kind);
-    NM_CMP_FIELD(a, b, addr_family);
-    if (compare_handle)
-        NM_CMP_FIELD(a, b, handle);
-    NM_CMP_FIELD(a, b, info);
-
-    if (nm_streq0(a->kind, "fq_codel")) {
-        NM_CMP_FIELD(a, b, fq_codel.limit);
-        NM_CMP_FIELD(a, b, fq_codel.flows);
-        NM_CMP_FIELD(a, b, fq_codel.target);
-        NM_CMP_FIELD(a, b, fq_codel.interval);
-        NM_CMP_FIELD(a, b, fq_codel.quantum);
-        NM_CMP_FIELD(a, b, fq_codel.ce_threshold);
-        NM_CMP_FIELD(a, b, fq_codel.memory_limit);
-        NM_CMP_FIELD_UNSAFE(a, b, fq_codel.ecn);
-    } else if (nm_streq0(a->kind, "sfq")) {
-        NM_CMP_FIELD(a, b, sfq.quantum);
-        NM_CMP_FIELD(a, b, sfq.perturb_period);
-        NM_CMP_FIELD(a, b, sfq.limit);
-        NM_CMP_FIELD(a, b, sfq.flows);
-        NM_CMP_FIELD(a, b, sfq.divisor);
-        NM_CMP_FIELD(a, b, sfq.depth);
-    } else if (nm_streq0(a->kind, "tbf")) {
-        NM_CMP_FIELD(a, b, tbf.rate);
-        NM_CMP_FIELD(a, b, tbf.burst);
-        NM_CMP_FIELD(a, b, tbf.limit);
-        NM_CMP_FIELD(a, b, tbf.latency);
-    }
-
-    return 0;
-}
-
-int
-nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b)
-{
-    return nm_platform_qdisc_cmp_full(a, b, TRUE);
-}
-
-const char *
-nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len)
-{
-    char  str_dev[TO_STRING_DEV_BUF_SIZE];
-    char  act_buf[300];
-    char *p;
-    gsize l;
-
-    if (!nm_utils_to_string_buffer_init_null(tfilter, &buf, &len))
-        return buf;
-
-    if (tfilter->action.kind) {
-        p = act_buf;
-        l = sizeof(act_buf);
-
-        nm_utils_strbuf_append(&p, &l, " \"%s\"", tfilter->action.kind);
-        if (nm_streq(tfilter->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) {
-            gs_free char *t = NULL;
-
-            nm_utils_strbuf_append(
-                &p,
-                &l,
-                " (\"%s\")",
-                nm_utils_str_utf8safe_escape(tfilter->action.kind,
-                                             NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL
-                                                 | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII,
-                                             &t));
-        } else if (nm_streq(tfilter->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) {
-            nm_utils_strbuf_append(&p,
-                                   &l,
-                                   "%s%s%s%s dev %d",
-                                   tfilter->action.mirred.ingress ? " ingress" : "",
-                                   tfilter->action.mirred.egress ? " egress" : "",
-                                   tfilter->action.mirred.mirror ? " mirror" : "",
-                                   tfilter->action.mirred.redirect ? " redirect" : "",
-                                   tfilter->action.mirred.ifindex);
-        }
-    } else
-        act_buf[0] = '\0';
-
-    g_snprintf(buf,
-               len,
-               "%s%s family %u handle %x parent %x info %x%s",
-               tfilter->kind,
-               _to_string_dev(NULL, tfilter->ifindex, str_dev, sizeof(str_dev)),
-               tfilter->addr_family,
-               tfilter->handle,
-               tfilter->parent,
-               tfilter->info,
-               act_buf);
-
-    return buf;
-}
-
-void
-nm_platform_tfilter_hash_update(const NMPlatformTfilter *obj, NMHashState *h)
-{
-    nm_hash_update_str0(h, obj->kind);
-    nm_hash_update_vals(h, obj->ifindex, obj->addr_family, obj->handle, obj->parent, obj->info);
-    if (obj->action.kind) {
-        nm_hash_update_str(h, obj->action.kind);
-        if (nm_streq(obj->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) {
-            nm_hash_update_strarr(h, obj->action.simple.sdata);
-        } else if (nm_streq(obj->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) {
-            nm_hash_update_vals(h,
-                                obj->action.mirred.ifindex,
-                                NM_HASH_COMBINE_BOOLS(guint8,
-                                                      obj->action.mirred.ingress,
-                                                      obj->action.mirred.egress,
-                                                      obj->action.mirred.mirror,
-                                                      obj->action.mirred.redirect));
-        }
-    }
-}
-
-int
-nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, ifindex);
-    NM_CMP_FIELD(a, b, parent);
-    NM_CMP_FIELD_STR_INTERNED(a, b, kind);
-    NM_CMP_FIELD(a, b, addr_family);
-    NM_CMP_FIELD(a, b, handle);
-    NM_CMP_FIELD(a, b, info);
-
-    NM_CMP_FIELD_STR_INTERNED(a, b, action.kind);
-    if (a->action.kind) {
-        if (nm_streq(a->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) {
-            NM_CMP_FIELD_STR(a, b, action.simple.sdata);
-        } else if (nm_streq(a->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) {
-            NM_CMP_FIELD(a, b, action.mirred.ifindex);
-            NM_CMP_FIELD_UNSAFE(a, b, action.mirred.ingress);
-            NM_CMP_FIELD_UNSAFE(a, b, action.mirred.egress);
-            NM_CMP_FIELD_UNSAFE(a, b, action.mirred.mirror);
-            NM_CMP_FIELD_UNSAFE(a, b, action.mirred.redirect);
-        }
-    }
-
-    return 0;
-}
-
-const char *
-nm_platform_vf_to_string(const NMPlatformVF *vf, char *buf, gsize len)
-{
-    char                 str_mac[128], mac[128];
-    char                 str_spoof_check[64];
-    char                 str_trust[64];
-    char                 str_min_tx_rate[64];
-    char                 str_max_tx_rate[64];
-    nm_auto_free_gstring GString *gstr_vlans = NULL;
-    guint                         i;
-
-    if (!nm_utils_to_string_buffer_init_null(vf, &buf, &len))
-        return buf;
-
-    if (vf->mac.len) {
-        _nm_utils_hwaddr_ntoa(vf->mac.data, vf->mac.len, TRUE, mac, sizeof(mac));
-        nm_sprintf_buf(str_mac, " mac %s", mac);
-    } else
-        str_mac[0] = '\0';
-
-    if (vf->num_vlans) {
-        gstr_vlans = g_string_new("");
-        for (i = 0; i < vf->num_vlans; i++) {
-            g_string_append_printf(gstr_vlans, " vlan %u", (unsigned) vf->vlans[i].id);
-            if (vf->vlans[i].qos)
-                g_string_append_printf(gstr_vlans, " qos %u", (unsigned) vf->vlans[i].qos);
-            if (vf->vlans[i].proto_ad)
-                g_string_append(gstr_vlans, " proto 802.1ad");
-        }
-    }
-
-    g_snprintf(buf,
-               len,
-               "%u"  /* index */
-               "%s"  /* MAC */
-               "%s"  /* spoof check */
-               "%s"  /* trust */
-               "%s"  /* min tx rate */
-               "%s"  /* max tx rate */
-               "%s", /* VLANs */
-               vf->index,
-               str_mac,
-               vf->spoofchk >= 0 ? nm_sprintf_buf(str_spoof_check, " spoofchk %d", vf->spoofchk)
-                                 : "",
-               vf->trust >= 0 ? nm_sprintf_buf(str_trust, " trust %d", vf->trust) : "",
-               vf->min_tx_rate
-                   ? nm_sprintf_buf(str_min_tx_rate, " min_tx_rate %u", (unsigned) vf->min_tx_rate)
-                   : "",
-               vf->max_tx_rate
-                   ? nm_sprintf_buf(str_max_tx_rate, " max_tx_rate %u", (unsigned) vf->max_tx_rate)
-                   : "",
-               gstr_vlans ? gstr_vlans->str : "");
-
-    return buf;
-}
-
-const char *
-nm_platform_bridge_vlan_to_string(const NMPlatformBridgeVlan *vlan, char *buf, gsize len)
-{
-    char str_vid_end[64];
-
-    if (!nm_utils_to_string_buffer_init_null(vlan, &buf, &len))
-        return buf;
-
-    g_snprintf(buf,
-               len,
-               "%u"
-               "%s"
-               "%s"
-               "%s",
-               vlan->vid_start,
-               vlan->vid_start != vlan->vid_end ? nm_sprintf_buf(str_vid_end, "-%u", vlan->vid_end)
-                                                : "",
-               vlan->pvid ? " PVID" : "",
-               vlan->untagged ? " untagged" : "");
-
-    return buf;
-}
-
-void
-nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->ifindex,
-                        obj->master,
-                        obj->parent,
-                        obj->n_ifi_flags,
-                        obj->mtu,
-                        obj->type,
-                        obj->arptype,
-                        obj->inet6_addr_gen_mode_inv,
-                        obj->inet6_token,
-                        obj->rx_packets,
-                        obj->rx_bytes,
-                        obj->tx_packets,
-                        obj->tx_bytes,
-                        NM_HASH_COMBINE_BOOLS(guint8, obj->connected, obj->initialized));
-    nm_hash_update_strarr(h, obj->name);
-    nm_hash_update_str0(h, obj->kind);
-    nm_hash_update_str0(h, obj->driver);
-    /* nm_hash_update_mem() also hashes the length obj->addr.len */
-    nm_hash_update_mem(h,
-                       obj->l_address.data,
-                       NM_MIN(obj->l_address.len, sizeof(obj->l_address.data)));
-    nm_hash_update_mem(h,
-                       obj->l_broadcast.data,
-                       NM_MIN(obj->l_broadcast.len, sizeof(obj->l_broadcast.data)));
-}
-
-int
-nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, ifindex);
-    NM_CMP_FIELD(a, b, type);
-    NM_CMP_FIELD_STR(a, b, name);
-    NM_CMP_FIELD(a, b, master);
-    NM_CMP_FIELD(a, b, parent);
-    NM_CMP_FIELD(a, b, n_ifi_flags);
-    NM_CMP_FIELD_UNSAFE(a, b, connected);
-    NM_CMP_FIELD(a, b, mtu);
-    NM_CMP_FIELD_BOOL(a, b, initialized);
-    NM_CMP_FIELD(a, b, arptype);
-    NM_CMP_FIELD(a, b, l_address.len);
-    NM_CMP_FIELD(a, b, l_broadcast.len);
-    NM_CMP_FIELD(a, b, inet6_addr_gen_mode_inv);
-    NM_CMP_FIELD_STR_INTERNED(a, b, kind);
-    NM_CMP_FIELD_STR_INTERNED(a, b, driver);
-    if (a->l_address.len)
-        NM_CMP_FIELD_MEMCMP_LEN(a, b, l_address.data, a->l_address.len);
-    if (a->l_broadcast.len)
-        NM_CMP_FIELD_MEMCMP_LEN(a, b, l_broadcast.data, a->l_broadcast.len);
-    NM_CMP_FIELD_MEMCMP(a, b, inet6_token);
-    NM_CMP_FIELD(a, b, rx_packets);
-    NM_CMP_FIELD(a, b, rx_bytes);
-    NM_CMP_FIELD(a, b, tx_packets);
-    NM_CMP_FIELD(a, b, tx_bytes);
-    return 0;
-}
-
-void
-nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->forward_delay,
-                        obj->hello_time,
-                        obj->max_age,
-                        obj->ageing_time,
-                        obj->priority,
-                        obj->vlan_protocol,
-                        obj->group_fwd_mask,
-                        obj->group_addr,
-                        obj->mcast_hash_max,
-                        obj->mcast_last_member_count,
-                        obj->mcast_startup_query_count,
-                        obj->mcast_last_member_interval,
-                        obj->mcast_membership_interval,
-                        obj->mcast_querier_interval,
-                        obj->mcast_query_interval,
-                        obj->mcast_router,
-                        obj->mcast_query_response_interval,
-                        obj->mcast_startup_query_interval,
-                        NM_HASH_COMBINE_BOOLS(guint8,
-                                              obj->stp_state,
-                                              obj->mcast_querier,
-                                              obj->mcast_query_use_ifaddr,
-                                              obj->mcast_snooping,
-                                              obj->vlan_stats_enabled));
-}
-
-int
-nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, forward_delay);
-    NM_CMP_FIELD(a, b, hello_time);
-    NM_CMP_FIELD(a, b, max_age);
-    NM_CMP_FIELD(a, b, ageing_time);
-    NM_CMP_FIELD_BOOL(a, b, stp_state);
-    NM_CMP_FIELD(a, b, priority);
-    NM_CMP_FIELD(a, b, vlan_protocol);
-    NM_CMP_FIELD_BOOL(a, b, vlan_stats_enabled);
-    NM_CMP_FIELD(a, b, group_fwd_mask);
-    NM_CMP_FIELD_MEMCMP(a, b, group_addr);
-    NM_CMP_FIELD_BOOL(a, b, mcast_snooping);
-    NM_CMP_FIELD(a, b, mcast_router);
-    NM_CMP_FIELD_BOOL(a, b, mcast_query_use_ifaddr);
-    NM_CMP_FIELD_BOOL(a, b, mcast_querier);
-    NM_CMP_FIELD(a, b, mcast_hash_max);
-    NM_CMP_FIELD(a, b, mcast_last_member_count);
-    NM_CMP_FIELD(a, b, mcast_startup_query_count);
-    NM_CMP_FIELD(a, b, mcast_last_member_interval);
-    NM_CMP_FIELD(a, b, mcast_membership_interval);
-    NM_CMP_FIELD(a, b, mcast_querier_interval);
-    NM_CMP_FIELD(a, b, mcast_query_interval);
-    NM_CMP_FIELD(a, b, mcast_query_response_interval);
-    NM_CMP_FIELD(a, b, mcast_startup_query_interval);
-
-    return 0;
-}
-
-void
-nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->local,
-                        obj->remote,
-                        obj->parent_ifindex,
-                        obj->input_flags,
-                        obj->output_flags,
-                        obj->input_key,
-                        obj->output_key,
-                        obj->ttl,
-                        obj->tos,
-                        (bool) obj->path_mtu_discovery,
-                        (bool) obj->is_tap);
-}
-
-int
-nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, parent_ifindex);
-    NM_CMP_FIELD(a, b, input_flags);
-    NM_CMP_FIELD(a, b, output_flags);
-    NM_CMP_FIELD(a, b, input_key);
-    NM_CMP_FIELD(a, b, output_key);
-    NM_CMP_FIELD(a, b, local);
-    NM_CMP_FIELD(a, b, remote);
-    NM_CMP_FIELD(a, b, ttl);
-    NM_CMP_FIELD(a, b, tos);
-    NM_CMP_FIELD_BOOL(a, b, path_mtu_discovery);
-    NM_CMP_FIELD_BOOL(a, b, is_tap);
-    return 0;
-}
-
-void
-nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h)
-{
-    nm_hash_update_val(h, obj->p_key);
-    nm_hash_update_str0(h, obj->mode);
-}
-
-int
-nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, p_key);
-    NM_CMP_FIELD_STR_INTERNED(a, b, mode);
-    return 0;
-}
-
-void
-nm_platform_lnk_ip6tnl_hash_update(const NMPlatformLnkIp6Tnl *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->local,
-                        obj->remote,
-                        obj->parent_ifindex,
-                        obj->ttl,
-                        obj->tclass,
-                        obj->encap_limit,
-                        obj->proto,
-                        obj->flow_label,
-                        obj->flags,
-                        obj->input_flags,
-                        obj->output_flags,
-                        obj->input_key,
-                        obj->output_key,
-                        (bool) obj->is_gre,
-                        (bool) obj->is_tap);
-}
-
-int
-nm_platform_lnk_ip6tnl_cmp(const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, parent_ifindex);
-    NM_CMP_FIELD_MEMCMP(a, b, local);
-    NM_CMP_FIELD_MEMCMP(a, b, remote);
-    NM_CMP_FIELD(a, b, ttl);
-    NM_CMP_FIELD(a, b, tclass);
-    NM_CMP_FIELD(a, b, encap_limit);
-    NM_CMP_FIELD(a, b, flow_label);
-    NM_CMP_FIELD(a, b, proto);
-    NM_CMP_FIELD(a, b, flags);
-    NM_CMP_FIELD(a, b, input_flags);
-    NM_CMP_FIELD(a, b, output_flags);
-    NM_CMP_FIELD(a, b, input_key);
-    NM_CMP_FIELD(a, b, output_key);
-    NM_CMP_FIELD_BOOL(a, b, is_gre);
-    NM_CMP_FIELD_BOOL(a, b, is_tap);
-    return 0;
-}
-
-void
-nm_platform_lnk_ipip_hash_update(const NMPlatformLnkIpIp *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->local,
-                        obj->remote,
-                        obj->parent_ifindex,
-                        obj->ttl,
-                        obj->tos,
-                        (bool) obj->path_mtu_discovery);
-}
-
-int
-nm_platform_lnk_ipip_cmp(const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, parent_ifindex);
-    NM_CMP_FIELD(a, b, local);
-    NM_CMP_FIELD(a, b, remote);
-    NM_CMP_FIELD(a, b, ttl);
-    NM_CMP_FIELD(a, b, tos);
-    NM_CMP_FIELD_BOOL(a, b, path_mtu_discovery);
-    return 0;
-}
-
-void
-nm_platform_lnk_macsec_hash_update(const NMPlatformLnkMacsec *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->parent_ifindex,
-                        obj->sci,
-                        obj->cipher_suite,
-                        obj->window,
-                        obj->icv_length,
-                        obj->encoding_sa,
-                        obj->validation,
-                        NM_HASH_COMBINE_BOOLS(guint8,
-                                              obj->encrypt,
-                                              obj->protect,
-                                              obj->include_sci,
-                                              obj->es,
-                                              obj->scb,
-                                              obj->replay_protect));
-}
-
-int
-nm_platform_lnk_macsec_cmp(const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, parent_ifindex);
-    NM_CMP_FIELD(a, b, sci);
-    NM_CMP_FIELD(a, b, icv_length);
-    NM_CMP_FIELD(a, b, cipher_suite);
-    NM_CMP_FIELD(a, b, window);
-    NM_CMP_FIELD(a, b, encoding_sa);
-    NM_CMP_FIELD(a, b, validation);
-    NM_CMP_FIELD_UNSAFE(a, b, encrypt);
-    NM_CMP_FIELD_UNSAFE(a, b, protect);
-    NM_CMP_FIELD_UNSAFE(a, b, include_sci);
-    NM_CMP_FIELD_UNSAFE(a, b, es);
-    NM_CMP_FIELD_UNSAFE(a, b, scb);
-    NM_CMP_FIELD_UNSAFE(a, b, replay_protect);
-    return 0;
-}
-
-void
-nm_platform_lnk_macvlan_hash_update(const NMPlatformLnkMacvlan *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h, obj->mode, NM_HASH_COMBINE_BOOLS(guint8, obj->no_promisc, obj->tap));
-}
-
-int
-nm_platform_lnk_macvlan_cmp(const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, mode);
-    NM_CMP_FIELD_UNSAFE(a, b, no_promisc);
-    NM_CMP_FIELD_UNSAFE(a, b, tap);
-    return 0;
-}
-
-void
-nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->local,
-                        obj->remote,
-                        obj->parent_ifindex,
-                        obj->flags,
-                        obj->ttl,
-                        obj->tos,
-                        obj->proto,
-                        (bool) obj->path_mtu_discovery);
-}
-
-int
-nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, parent_ifindex);
-    NM_CMP_FIELD(a, b, local);
-    NM_CMP_FIELD(a, b, remote);
-    NM_CMP_FIELD(a, b, ttl);
-    NM_CMP_FIELD(a, b, tos);
-    NM_CMP_FIELD_BOOL(a, b, path_mtu_discovery);
-    NM_CMP_FIELD(a, b, flags);
-    NM_CMP_FIELD(a, b, proto);
-    return 0;
-}
-
-void
-nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->type,
-                        obj->owner,
-                        obj->group,
-                        NM_HASH_COMBINE_BOOLS(guint8,
-                                              obj->owner_valid,
-                                              obj->group_valid,
-                                              obj->pi,
-                                              obj->vnet_hdr,
-                                              obj->multi_queue,
-                                              obj->persist));
-}
-
-int
-nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, type);
-    NM_CMP_FIELD(a, b, owner);
-    NM_CMP_FIELD(a, b, group);
-    NM_CMP_FIELD_BOOL(a, b, owner_valid);
-    NM_CMP_FIELD_BOOL(a, b, group_valid);
-    NM_CMP_FIELD_BOOL(a, b, pi);
-    NM_CMP_FIELD_BOOL(a, b, vnet_hdr);
-    NM_CMP_FIELD_BOOL(a, b, multi_queue);
-    NM_CMP_FIELD_BOOL(a, b, persist);
-    return 0;
-}
-
-void
-nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h, obj->id, obj->flags);
-}
-
-int
-nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, id);
-    NM_CMP_FIELD(a, b, flags);
-    return 0;
-}
-
-void
-nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h, obj->table);
-}
-
-int
-nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, table);
-    return 0;
-}
-
-void
-nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->group6,
-                        obj->local6,
-                        obj->group,
-                        obj->local,
-                        obj->parent_ifindex,
-                        obj->id,
-                        obj->ageing,
-                        obj->limit,
-                        obj->dst_port,
-                        obj->src_port_min,
-                        obj->src_port_max,
-                        obj->tos,
-                        obj->ttl,
-                        NM_HASH_COMBINE_BOOLS(guint8,
-                                              obj->learning,
-                                              obj->proxy,
-                                              obj->rsc,
-                                              obj->l2miss,
-                                              obj->l3miss));
-}
-
-int
-nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, parent_ifindex);
-    NM_CMP_FIELD(a, b, id);
-    NM_CMP_FIELD(a, b, group);
-    NM_CMP_FIELD(a, b, local);
-    NM_CMP_FIELD_MEMCMP(a, b, group6);
-    NM_CMP_FIELD_MEMCMP(a, b, local6);
-    NM_CMP_FIELD(a, b, tos);
-    NM_CMP_FIELD(a, b, ttl);
-    NM_CMP_FIELD_BOOL(a, b, learning);
-    NM_CMP_FIELD(a, b, ageing);
-    NM_CMP_FIELD(a, b, limit);
-    NM_CMP_FIELD(a, b, dst_port);
-    NM_CMP_FIELD(a, b, src_port_min);
-    NM_CMP_FIELD(a, b, src_port_max);
-    NM_CMP_FIELD_BOOL(a, b, proxy);
-    NM_CMP_FIELD_BOOL(a, b, rsc);
-    NM_CMP_FIELD_BOOL(a, b, l2miss);
-    NM_CMP_FIELD_BOOL(a, b, l3miss);
-    return 0;
-}
-
-void
-nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h, obj->listen_port, obj->fwmark);
-    nm_hash_update(h, obj->private_key, sizeof(obj->private_key));
-    nm_hash_update(h, obj->public_key, sizeof(obj->public_key));
-}
-
-int
-nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, listen_port);
-    NM_CMP_FIELD(a, b, fwmark);
-    NM_CMP_FIELD_MEMCMP(a, b, private_key);
-    NM_CMP_FIELD_MEMCMP(a, b, public_key);
-    return 0;
-}
-
-static int
-_address_pretty_sort_get_prio_4(in_addr_t addr)
-{
-    if (nm_utils_ip4_address_is_link_local(addr))
-        return 0;
-    return 1;
-}
-
-int
-nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1,
-                                        const NMPlatformIP4Address *a2)
-{
-    in_addr_t n1;
-    in_addr_t n2;
-
-    nm_assert(a1);
-    nm_assert(a2);
-
-    /* Sort by address type. For example link local will
-     * be sorted *after* a global address. */
-    NM_CMP_DIRECT(_address_pretty_sort_get_prio_4(a2->address),
-                  _address_pretty_sort_get_prio_4(a1->address));
-
-    /* Sort the addresses based on their source. */
-    NM_CMP_DIRECT(a2->addr_source, a1->addr_source);
-
-    NM_CMP_DIRECT((a2->label[0] == '\0'), (a1->label[0] == '\0'));
-
-    /* Finally, sort addresses lexically. We compare only the
-     * network part so that the order of addresses in the same
-     * subnet (and thus also the primary/secondary role) is
-     * preserved.
-     */
-    n1 = a1->address & _nm_utils_ip4_prefix_to_netmask(a1->plen);
-    n2 = a2->address & _nm_utils_ip4_prefix_to_netmask(a2->plen);
-    NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32));
-    return 0;
-}
-
-static int
-_address_pretty_sort_get_prio_6(const struct in6_addr *addr)
-{
-    if (IN6_IS_ADDR_V4MAPPED(addr))
-        return 0;
-    if (IN6_IS_ADDR_V4COMPAT(addr))
-        return 1;
-    if (IN6_IS_ADDR_UNSPECIFIED(addr))
-        return 2;
-    if (IN6_IS_ADDR_LOOPBACK(addr))
-        return 3;
-    if (IN6_IS_ADDR_LINKLOCAL(addr))
-        return 4;
-    if (IN6_IS_ADDR_SITELOCAL(addr))
-        return 5;
-    return 6;
-}
-
-int
-nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1,
-                                        const NMPlatformIP6Address *a2,
-                                        gboolean                    prefer_temp)
-{
-    gboolean ipv6_privacy1;
-    gboolean ipv6_privacy2;
-
-    nm_assert(a1);
-    nm_assert(a2);
-
-    /* tentative addresses are always sorted back... */
-    /* sort tentative addresses after non-tentative. */
-    NM_CMP_DIRECT(NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE),
-                  NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_TENTATIVE));
-
-    /* Sort by address type. For example link local will
-     * be sorted *after* site local or global. */
-    NM_CMP_DIRECT(_address_pretty_sort_get_prio_6(&a2->address),
-                  _address_pretty_sort_get_prio_6(&a1->address));
-
-    ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY);
-    ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY);
-    if (ipv6_privacy1 || ipv6_privacy2) {
-        gboolean public1 = TRUE;
-        gboolean public2 = TRUE;
-
-        if (ipv6_privacy1) {
-            if (a1->n_ifa_flags & IFA_F_TEMPORARY)
-                public1 = prefer_temp;
-            else
-                public1 = !prefer_temp;
-        }
-        if (ipv6_privacy2) {
-            if (a2->n_ifa_flags & IFA_F_TEMPORARY)
-                public2 = prefer_temp;
-            else
-                public2 = !prefer_temp;
-        }
-
-        NM_CMP_DIRECT(public2, public1);
-    }
-
-    /* Sort the addresses based on their source. */
-    NM_CMP_DIRECT(a2->addr_source, a1->addr_source);
-
-    /* sort permanent addresses before non-permanent. */
-    NM_CMP_DIRECT(NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_PERMANENT),
-                  NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_PERMANENT));
-
-    /* finally sort addresses lexically */
-    NM_CMP_DIRECT_IN6ADDR(&a1->address, &a2->address);
-    NM_CMP_DIRECT_MEMCMP(a1, a2, sizeof(*a1));
-    return 0;
-}
-
-void
-nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->ifindex,
-                        obj->addr_source,
-                        obj->use_ip4_broadcast_address ? obj->broadcast_address : ((in_addr_t) 0u),
-                        obj->timestamp,
-                        obj->lifetime,
-                        obj->preferred,
-                        obj->n_ifa_flags,
-                        obj->plen,
-                        obj->address,
-                        obj->peer_address,
-                        NM_HASH_COMBINE_BOOLS(guint8,
-                                              obj->external,
-                                              obj->use_ip4_broadcast_address,
-                                              obj->ip4acd_not_ready));
-    nm_hash_update_strarr(h, obj->label);
-}
-
-int
-nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
-{
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, ifindex);
-    NM_CMP_FIELD(a, b, address);
-    NM_CMP_FIELD(a, b, plen);
-    NM_CMP_FIELD(a, b, peer_address);
-    NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address);
-    if (a->use_ip4_broadcast_address)
-        NM_CMP_FIELD(a, b, broadcast_address);
-    NM_CMP_FIELD(a, b, addr_source);
-    NM_CMP_FIELD(a, b, timestamp);
-    NM_CMP_FIELD(a, b, lifetime);
-    NM_CMP_FIELD(a, b, preferred);
-    NM_CMP_FIELD(a, b, n_ifa_flags);
-    NM_CMP_FIELD_STR(a, b, label);
-    NM_CMP_FIELD_UNSAFE(a, b, external);
-    NM_CMP_FIELD_UNSAFE(a, b, ip4acd_not_ready);
-    return 0;
-}
-
-void
-nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h)
-{
-    nm_hash_update_vals(h,
-                        obj->ifindex,
-                        obj->addr_source,
-                        obj->timestamp,
-                        obj->lifetime,
-                        obj->preferred,
-                        obj->n_ifa_flags,
-                        obj->plen,
-                        obj->address,
-                        obj->peer_address,
-                        NM_HASH_COMBINE_BOOLS(guint8, obj->external));
-}
-
-int
-nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
-{
-    const struct in6_addr *p_a, *p_b;
-
-    NM_CMP_SELF(a, b);
-    NM_CMP_FIELD(a, b, ifindex);
-    NM_CMP_FIELD_MEMCMP(a, b, address);
-    NM_CMP_FIELD(a, b, plen);
-    p_a = nm_platform_ip6_address_get_peer(a);
-    p_b = nm_platform_ip6_address_get_peer(b);
-    NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a));
-    NM_CMP_FIELD(a, b, addr_source);
-    NM_CMP_FIELD(a, b, timestamp);
-    NM_CMP_FIELD(a, b, lifetime);
-    NM_CMP_FIELD(a, b, preferred);
-    NM_CMP_FIELD(a, b, n_ifa_flags);
-    NM_CMP_FIELD_UNSAFE(a, b, external);
-    return 0;
-}
-
-void
-nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
-                                  NMPlatformIPRouteCmpType  cmp_type,
-                                  NMHashState *             h)
-{
-    switch (cmp_type) {
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
-        nm_hash_update_vals(
-            h,
-            nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
-            nm_utils_ip4_address_clear_host_address(obj->network, obj->plen),
-            obj->plen,
-            obj->metric,
-            obj->tos,
-            NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any));
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
-        nm_hash_update_vals(
-            h,
-            obj->type_coerced,
-            nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
-            nm_utils_ip4_address_clear_host_address(obj->network, obj->plen),
-            obj->plen,
-            obj->metric,
-            obj->tos,
-            /* on top of WEAK_ID: */
-            obj->ifindex,
-            nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source),
-            _ip_route_scope_inv_get_normalized(obj),
-            obj->gateway,
-            obj->mss,
-            obj->pref_src,
-            obj->window,
-            obj->cwnd,
-            obj->initcwnd,
-            obj->initrwnd,
-            obj->mtu,
-            obj->r_rtm_flags & RTNH_F_ONLINK,
-            NM_HASH_COMBINE_BOOLS(guint8,
-                                  obj->metric_any,
-                                  obj->table_any,
-                                  obj->lock_window,
-                                  obj->lock_cwnd,
-                                  obj->lock_initcwnd,
-                                  obj->lock_initrwnd,
-                                  obj->lock_mtu));
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
-        nm_hash_update_vals(
-            h,
-            obj->type_coerced,
-            nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
-            obj->ifindex,
-            nm_utils_ip4_address_clear_host_address(obj->network, obj->plen),
-            obj->plen,
-            obj->metric,
-            obj->gateway,
-            nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source),
-            _ip_route_scope_inv_get_normalized(obj),
-            obj->tos,
-            obj->mss,
-            obj->pref_src,
-            obj->window,
-            obj->cwnd,
-            obj->initcwnd,
-            obj->initrwnd,
-            obj->mtu,
-            obj->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK),
-            NM_HASH_COMBINE_BOOLS(guint8,
-                                  obj->metric_any,
-                                  obj->table_any,
-                                  obj->lock_window,
-                                  obj->lock_cwnd,
-                                  obj->lock_initcwnd,
-                                  obj->lock_initrwnd,
-                                  obj->lock_mtu));
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
-        nm_hash_update_vals(h,
-                            obj->type_coerced,
-                            obj->table_coerced,
-                            obj->ifindex,
-                            obj->network,
-                            obj->plen,
-                            obj->metric,
-                            obj->gateway,
-                            obj->rt_source,
-                            obj->scope_inv,
-                            obj->tos,
-                            obj->mss,
-                            obj->pref_src,
-                            obj->window,
-                            obj->cwnd,
-                            obj->initcwnd,
-                            obj->initrwnd,
-                            obj->mtu,
-                            obj->r_rtm_flags,
-                            NM_HASH_COMBINE_BOOLS(guint8,
-                                                  obj->metric_any,
-                                                  obj->table_any,
-                                                  obj->lock_window,
-                                                  obj->lock_cwnd,
-                                                  obj->lock_initcwnd,
-                                                  obj->lock_initrwnd,
-                                                  obj->lock_mtu));
-        break;
-    }
-}
-
-int
-nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
-                          const NMPlatformIP4Route *b,
-                          NMPlatformIPRouteCmpType  cmp_type)
-{
-    NM_CMP_SELF(a, b);
-    switch (cmp_type) {
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
-        NM_CMP_FIELD_UNSAFE(a, b, table_any);
-        NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)),
-                      nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b)));
-        NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen));
-        NM_CMP_FIELD(a, b, plen);
-        NM_CMP_FIELD_UNSAFE(a, b, metric_any);
-        NM_CMP_FIELD(a, b, metric);
-        NM_CMP_FIELD(a, b, tos);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
-            NM_CMP_FIELD(a, b, ifindex);
-            NM_CMP_FIELD(a, b, type_coerced);
-            NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source),
-                          nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source));
-            NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a),
-                          _ip_route_scope_inv_get_normalized(b));
-            NM_CMP_FIELD(a, b, gateway);
-            NM_CMP_FIELD(a, b, mss);
-            NM_CMP_FIELD(a, b, pref_src);
-            NM_CMP_FIELD(a, b, window);
-            NM_CMP_FIELD(a, b, cwnd);
-            NM_CMP_FIELD(a, b, initcwnd);
-            NM_CMP_FIELD(a, b, initrwnd);
-            NM_CMP_FIELD(a, b, mtu);
-            NM_CMP_DIRECT(a->r_rtm_flags & RTNH_F_ONLINK, b->r_rtm_flags & RTNH_F_ONLINK);
-            NM_CMP_FIELD_UNSAFE(a, b, lock_window);
-            NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd);
-            NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd);
-            NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd);
-            NM_CMP_FIELD_UNSAFE(a, b, lock_mtu);
-        }
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
-        NM_CMP_FIELD(a, b, type_coerced);
-        NM_CMP_FIELD_UNSAFE(a, b, table_any);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
-            NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)),
-                          nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b)));
-        } else
-            NM_CMP_FIELD(a, b, table_coerced);
-        NM_CMP_FIELD(a, b, ifindex);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
-            NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen));
-        else
-            NM_CMP_FIELD(a, b, network);
-        NM_CMP_FIELD(a, b, plen);
-        NM_CMP_FIELD_UNSAFE(a, b, metric_any);
-        NM_CMP_FIELD(a, b, metric);
-        NM_CMP_FIELD(a, b, gateway);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
-            NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source),
-                          nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source));
-            NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a),
-                          _ip_route_scope_inv_get_normalized(b));
-        } else {
-            NM_CMP_FIELD(a, b, rt_source);
-            NM_CMP_FIELD(a, b, scope_inv);
-        }
-        NM_CMP_FIELD(a, b, mss);
-        NM_CMP_FIELD(a, b, pref_src);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
-            NM_CMP_DIRECT(a->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK),
-                          b->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK));
-        } else
-            NM_CMP_FIELD(a, b, r_rtm_flags);
-        NM_CMP_FIELD(a, b, tos);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_window);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_mtu);
-        NM_CMP_FIELD(a, b, window);
-        NM_CMP_FIELD(a, b, cwnd);
-        NM_CMP_FIELD(a, b, initcwnd);
-        NM_CMP_FIELD(a, b, initrwnd);
-        NM_CMP_FIELD(a, b, mtu);
-        break;
-    }
-    return 0;
-}
-
-void
-nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
-                                  NMPlatformIPRouteCmpType  cmp_type,
-                                  NMHashState *             h)
-{
-    struct in6_addr a1, a2;
-
-    switch (cmp_type) {
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
-        nm_hash_update_vals(
-            h,
-            nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
-            *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen),
-            obj->plen,
-            obj->metric,
-            *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen),
-            obj->src_plen,
-            NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any));
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
-        nm_hash_update_vals(
-            h,
-            obj->type_coerced,
-            nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
-            *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen),
-            obj->plen,
-            obj->metric,
-            *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen),
-            obj->src_plen,
-            NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any),
-            /* on top of WEAK_ID: */
-            obj->ifindex,
-            obj->gateway);
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
-        nm_hash_update_vals(
-            h,
-            obj->type_coerced,
-            nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
-            obj->ifindex,
-            *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen),
-            obj->plen,
-            obj->metric,
-            obj->gateway,
-            obj->pref_src,
-            *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen),
-            obj->src_plen,
-            nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source),
-            obj->mss,
-            obj->r_rtm_flags & RTM_F_CLONED,
-            NM_HASH_COMBINE_BOOLS(guint8,
-                                  obj->metric_any,
-                                  obj->table_any,
-                                  obj->lock_window,
-                                  obj->lock_cwnd,
-                                  obj->lock_initcwnd,
-                                  obj->lock_initrwnd,
-                                  obj->lock_mtu),
-            obj->window,
-            obj->cwnd,
-            obj->initcwnd,
-            obj->initrwnd,
-            obj->mtu,
-            _route_pref_normalize(obj->rt_pref));
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
-        nm_hash_update_vals(h,
-                            obj->type_coerced,
-                            obj->table_coerced,
-                            obj->ifindex,
-                            obj->network,
-                            obj->metric,
-                            obj->gateway,
-                            obj->pref_src,
-                            obj->src,
-                            obj->src_plen,
-                            obj->rt_source,
-                            obj->mss,
-                            obj->r_rtm_flags,
-                            NM_HASH_COMBINE_BOOLS(guint8,
-                                                  obj->metric_any,
-                                                  obj->table_any,
-                                                  obj->lock_window,
-                                                  obj->lock_cwnd,
-                                                  obj->lock_initcwnd,
-                                                  obj->lock_initrwnd,
-                                                  obj->lock_mtu),
-                            obj->window,
-                            obj->cwnd,
-                            obj->initcwnd,
-                            obj->initrwnd,
-                            obj->mtu,
-                            obj->rt_pref);
-        break;
-    }
-}
-
-int
-nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
-                          const NMPlatformIP6Route *b,
-                          NMPlatformIPRouteCmpType  cmp_type)
-{
-    NM_CMP_SELF(a, b);
-    switch (cmp_type) {
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
-        NM_CMP_FIELD_UNSAFE(a, b, table_any);
-        NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)),
-                      nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b)));
-        NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen));
-        NM_CMP_FIELD(a, b, plen);
-        NM_CMP_FIELD_UNSAFE(a, b, metric_any);
-        NM_CMP_FIELD(a, b, metric);
-        NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen));
-        NM_CMP_FIELD(a, b, src_plen);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
-            NM_CMP_FIELD(a, b, ifindex);
-            NM_CMP_FIELD(a, b, type_coerced);
-            NM_CMP_FIELD_IN6ADDR(a, b, gateway);
-        }
-        break;
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
-    case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
-        NM_CMP_FIELD(a, b, type_coerced);
-        NM_CMP_FIELD_UNSAFE(a, b, table_any);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
-            NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)),
-                          nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b)));
-        } else
-            NM_CMP_FIELD(a, b, table_coerced);
-        NM_CMP_FIELD(a, b, ifindex);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
-            NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen));
-        else
-            NM_CMP_FIELD_IN6ADDR(a, b, network);
-        NM_CMP_FIELD(a, b, plen);
-        NM_CMP_FIELD_UNSAFE(a, b, metric_any);
-        NM_CMP_FIELD(a, b, metric);
-        NM_CMP_FIELD_IN6ADDR(a, b, gateway);
-        NM_CMP_FIELD_IN6ADDR(a, b, pref_src);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
-            NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen));
-            NM_CMP_FIELD(a, b, src_plen);
-            NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source),
-                          nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source));
-        } else {
-            NM_CMP_FIELD_IN6ADDR(a, b, src);
-            NM_CMP_FIELD(a, b, src_plen);
-            NM_CMP_FIELD(a, b, rt_source);
-        }
-        NM_CMP_FIELD(a, b, mss);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
-            NM_CMP_DIRECT(a->r_rtm_flags & RTM_F_CLONED, b->r_rtm_flags & RTM_F_CLONED);
-        } else
-            NM_CMP_FIELD(a, b, r_rtm_flags);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_window);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd);
-        NM_CMP_FIELD_UNSAFE(a, b, lock_mtu);
-        NM_CMP_FIELD(a, b, window);
-        NM_CMP_FIELD(a, b, cwnd);
-        NM_CMP_FIELD(a, b, initcwnd);
-        NM_CMP_FIELD(a, b, initrwnd);
-        NM_CMP_FIELD(a, b, mtu);
-        if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
-            NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref));
-        else
-            NM_CMP_FIELD(a, b, rt_pref);
-        break;
-    }
-    return 0;
-}
-
-#define _ROUTING_RULE_FLAGS_IGNORE \
-    (FIB_RULE_UNRESOLVED | FIB_RULE_IIF_DETACHED | FIB_RULE_OIF_DETACHED)
-
-#define _routing_rule_compare(cmp_type, kernel_support_type) \
-    ((cmp_type) == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL    \
-     || nm_platform_kernel_support_get(kernel_support_type))
-
-void
-nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj,
-                                     NMPlatformRoutingRuleCmpType cmp_type,
-                                     NMHashState *                h)
-{
-    gboolean cmp_full = TRUE;
-    gsize    addr_size;
-    guint32  flags_mask = G_MAXUINT32;
-
-    if (G_UNLIKELY(!NM_IN_SET(obj->addr_family, AF_INET, AF_INET6))) {
-        /* the address family is not one of the supported ones. That means, the
-         * instance will only compare equal to itself (pointer-equality). */
-        nm_hash_update_val(h, (gconstpointer) obj);
-        return;
-    }
-
-    switch (cmp_type) {
-    case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID:
-
-        flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE;
-
-        /* fall-through */
-    case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY:
-
-        cmp_full = FALSE;
-
-        /* fall-through */
-    case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL:
-
-        nm_hash_update_vals(
-            h,
-            obj->addr_family,
-            obj->tun_id,
-            obj->table,
-            obj->flags & flags_mask,
-            obj->priority,
-            obj->fwmark,
-            obj->fwmask,
-            ((cmp_full
-              || (cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY
-                  && obj->action == FR_ACT_GOTO))
-                 ? obj->goto_target
-                 : (guint32) 0u),
-            ((cmp_full || obj->addr_family == AF_INET) ? obj->flow : (guint32) 0u),
-            NM_HASH_COMBINE_BOOLS(
-                guint8,
-                (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)
-                     ? obj->uid_range_has
-                     : FALSE)),
-            obj->suppress_prefixlen_inverse,
-            obj->suppress_ifgroup_inverse,
-            (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)
-                 ? (cmp_full ? (guint16) obj->l3mdev : (guint16) !!obj->l3mdev)
-                 : G_MAXUINT16),
-            obj->action,
-            obj->tos,
-            obj->src_len,
-            obj->dst_len,
-            (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)
-                 ? (guint16) obj->protocol
-                 : G_MAXUINT16));
-        addr_size = nm_utils_addr_family_to_size(obj->addr_family);
-        if (cmp_full || obj->src_len > 0)
-            nm_hash_update(h, &obj->src, addr_size);
-        if (cmp_full || obj->dst_len > 0)
-            nm_hash_update(h, &obj->dst, addr_size);
-        if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) {
-            if (cmp_full || obj->uid_range_has)
-                nm_hash_update_valp(h, &obj->uid_range);
-        }
-        if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) {
-            nm_hash_update_val(h, obj->ip_proto);
-            nm_hash_update_valp(h, &obj->sport_range);
-            nm_hash_update_valp(h, &obj->dport_range);
-        }
-        nm_hash_update_str(h, obj->iifname);
-        nm_hash_update_str(h, obj->oifname);
-        return;
-    }
-
-    nm_assert_not_reached();
-}
-
-int
-nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a,
-                             const NMPlatformRoutingRule *b,
-                             NMPlatformRoutingRuleCmpType cmp_type)
-{
-    gboolean cmp_full = TRUE;
-    gsize    addr_size;
-    bool     valid;
-    guint32  flags_mask = G_MAXUINT32;
-
-    NM_CMP_SELF(a, b);
-
-    valid = NM_IN_SET(a->addr_family, AF_INET, AF_INET6);
-    NM_CMP_DIRECT(valid, (bool) NM_IN_SET(b->addr_family, AF_INET, AF_INET6));
-
-    if (G_UNLIKELY(!valid)) {
-        /* the address family is not one of the supported ones. That means, the
-         * instance will only compare equal to itself. */
-        NM_CMP_DIRECT((uintptr_t) a, (uintptr_t) b);
-        nm_assert_not_reached();
-        return 0;
-    }
-
-    switch (cmp_type) {
-    case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID:
-
-        flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE;
-
-        /* fall-through */
-    case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY:
-
-        cmp_full = FALSE;
-
-        /* fall-through */
-    case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL:
-        NM_CMP_FIELD(a, b, addr_family);
-        NM_CMP_FIELD(a, b, action);
-        NM_CMP_FIELD(a, b, priority);
-        NM_CMP_FIELD(a, b, tun_id);
-
-        if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) {
-            if (cmp_full)
-                NM_CMP_FIELD(a, b, l3mdev);
-            else
-                NM_CMP_FIELD_BOOL(a, b, l3mdev);
-        }
-
-        NM_CMP_FIELD(a, b, table);
-
-        NM_CMP_DIRECT(a->flags & flags_mask, b->flags & flags_mask);
-
-        NM_CMP_FIELD(a, b, fwmark);
-        NM_CMP_FIELD(a, b, fwmask);
-
-        if (cmp_full
-            || (cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY
-                && a->action == FR_ACT_GOTO))
-            NM_CMP_FIELD(a, b, goto_target);
-
-        NM_CMP_FIELD(a, b, suppress_prefixlen_inverse);
-        NM_CMP_FIELD(a, b, suppress_ifgroup_inverse);
-        NM_CMP_FIELD(a, b, tos);
-
-        if (cmp_full || a->addr_family == AF_INET)
-            NM_CMP_FIELD(a, b, flow);
-
-        if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL))
-            NM_CMP_FIELD(a, b, protocol);
-
-        if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) {
-            NM_CMP_FIELD(a, b, ip_proto);
-            NM_CMP_FIELD(a, b, sport_range.start);
-            NM_CMP_FIELD(a, b, sport_range.end);
-            NM_CMP_FIELD(a, b, dport_range.start);
-            NM_CMP_FIELD(a, b, dport_range.end);
-        }
-
-        addr_size = nm_utils_addr_family_to_size(a->addr_family);
-
-        NM_CMP_FIELD(a, b, src_len);
-        if (cmp_full || a->src_len > 0)
-            NM_CMP_FIELD_MEMCMP_LEN(a, b, src, addr_size);
-
-        NM_CMP_FIELD(a, b, dst_len);
-        if (cmp_full || a->dst_len > 0)
-            NM_CMP_FIELD_MEMCMP_LEN(a, b, dst, addr_size);
-
-        if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) {
-            NM_CMP_FIELD_UNSAFE(a, b, uid_range_has);
-            if (cmp_full || a->uid_range_has) {
-                NM_CMP_FIELD(a, b, uid_range.start);
-                NM_CMP_FIELD(a, b, uid_range.end);
-            }
-        }
-
-        NM_CMP_FIELD_STR(a, b, iifname);
-        NM_CMP_FIELD_STR(a, b, oifname);
-        return 0;
-    }
-
-    nm_assert_not_reached();
-    return 0;
-}
-
-/**
- * nm_platform_ip_address_cmp_expiry:
- * @a: a NMPlatformIPAddress to compare
- * @b: the other NMPlatformIPAddress to compare
- *
- * Compares two addresses and returns which one has a longer remaining lifetime.
- * If both addresses have the same lifetime, look at the remaining preferred time.
- *
- * For comparison, only the timestamp, lifetime and preferred fields are considered.
- * If they compare equal (== 0), their other fields were not considered.
- *
- * Returns: -1, 0, or 1 according to the comparison
- **/
-int
-nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b)
-{
-    gint64 ta = 0, tb = 0;
-
-    NM_CMP_SELF(a, b);
-
-    if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0)
-        ta = G_MAXINT64;
-    else if (a->timestamp)
-        ta = ((gint64) a->timestamp) + a->lifetime;
-
-    if (b->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || b->lifetime == 0)
-        tb = G_MAXINT64;
-    else if (b->timestamp)
-        tb = ((gint64) b->timestamp) + b->lifetime;
-
-    if (ta == tb) {
-        /* if the lifetime is equal, compare the preferred time. */
-        ta = tb = 0;
-
-        if (a->preferred == NM_PLATFORM_LIFETIME_PERMANENT
-            || a->lifetime == 0 /* lifetime==0 means permanent! */)
-            ta = G_MAXINT64;
-        else if (a->timestamp)
-            ta = ((gint64) a->timestamp) + a->preferred;
-
-        if (b->preferred == NM_PLATFORM_LIFETIME_PERMANENT || b->lifetime == 0)
-            tb = G_MAXINT64;
-        else if (b->timestamp)
-            tb = ((gint64) b->timestamp) + b->preferred;
-
-        if (ta == tb)
-            return 0;
-    }
-
-    return ta < tb ? -1 : 1;
-}
-
-/*****************************************************************************/
-
-GHashTable *
-nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex)
-{
-    const NMDedupMultiHeadEntry *head_entry;
-    NMDedupMultiIter             iter;
-    const NMPObject *            obj;
-    NMPLookup                    lookup;
-    GHashTable *                 hash;
-
-    g_return_val_if_fail(NM_IS_PLATFORM(self), NULL);
-    g_return_val_if_fail(ifindex > 0, NULL);
-
-    nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex);
-
-    head_entry = nmp_cache_lookup(NM_PLATFORM_GET_PRIVATE(self)->cache, &lookup);
-
-    if (!head_entry)
-        return NULL;
-
-    hash = g_hash_table_new(nm_direct_hash, NULL);
-
-    nmp_cache_iter_for_each (&iter, head_entry, &obj) {
-        const NMPlatformIP4Address *a = NMP_OBJECT_CAST_IP4_ADDRESS(obj);
-
-        g_hash_table_add(hash, GUINT_TO_POINTER(a->address));
-    }
-
-    return hash;
-}
-
-/*****************************************************************************/
-
-const char *
-nm_platform_signal_change_type_to_string(NMPlatformSignalChangeType change_type)
-{
-    switch (change_type) {
-    case NM_PLATFORM_SIGNAL_ADDED:
-        return "added";
-    case NM_PLATFORM_SIGNAL_CHANGED:
-        return "changed";
-    case NM_PLATFORM_SIGNAL_REMOVED:
-        return "removed";
-    default:
-        g_return_val_if_reached("UNKNOWN");
-    }
-}
-
-static void
-log_link(NMPlatform *               self,
-         NMPObjectType              obj_type,
-         int                        ifindex,
-         NMPlatformLink *           device,
-         NMPlatformSignalChangeType change_type,
-         gpointer                   user_data)
-{
-    _LOG3D("signal: link %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_link_to_string(device, NULL, 0));
-}
-
-static void
-log_ip4_address(NMPlatform *               self,
-                NMPObjectType              obj_type,
-                int                        ifindex,
-                NMPlatformIP4Address *     address,
-                NMPlatformSignalChangeType change_type,
-                gpointer                   user_data)
-{
-    _LOG3D("signal: address 4 %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_ip4_address_to_string(address, NULL, 0));
-}
-
-static void
-log_ip6_address(NMPlatform *               self,
-                NMPObjectType              obj_type,
-                int                        ifindex,
-                NMPlatformIP6Address *     address,
-                NMPlatformSignalChangeType change_type,
-                gpointer                   user_data)
-{
-    _LOG3D("signal: address 6 %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_ip6_address_to_string(address, NULL, 0));
-}
-
-static void
-log_ip4_route(NMPlatform *               self,
-              NMPObjectType              obj_type,
-              int                        ifindex,
-              NMPlatformIP4Route *       route,
-              NMPlatformSignalChangeType change_type,
-              gpointer                   user_data)
-{
-    _LOG3D("signal: route   4 %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_ip4_route_to_string(route, NULL, 0));
-}
-
-static void
-log_ip6_route(NMPlatform *               self,
-              NMPObjectType              obj_type,
-              int                        ifindex,
-              NMPlatformIP6Route *       route,
-              NMPlatformSignalChangeType change_type,
-              gpointer                   user_data)
-{
-    _LOG3D("signal: route   6 %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_ip6_route_to_string(route, NULL, 0));
-}
-
-static void
-log_routing_rule(NMPlatform *               self,
-                 NMPObjectType              obj_type,
-                 int                        ifindex,
-                 NMPlatformRoutingRule *    routing_rule,
-                 NMPlatformSignalChangeType change_type,
-                 gpointer                   user_data)
-{
-    /* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */
-    _LOG3D("signal: rt-rule %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_routing_rule_to_string(routing_rule, NULL, 0));
-}
-
-static void
-log_qdisc(NMPlatform *               self,
-          NMPObjectType              obj_type,
-          int                        ifindex,
-          NMPlatformQdisc *          qdisc,
-          NMPlatformSignalChangeType change_type,
-          gpointer                   user_data)
-{
-    _LOG3D("signal: qdisc %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_qdisc_to_string(qdisc, NULL, 0));
-}
-
-static void
-log_tfilter(NMPlatform *               self,
-            NMPObjectType              obj_type,
-            int                        ifindex,
-            NMPlatformTfilter *        tfilter,
-            NMPlatformSignalChangeType change_type,
-            gpointer                   user_data)
-{
-    _LOG3D("signal: tfilter %7s: %s",
-           nm_platform_signal_change_type_to_string(change_type),
-           nm_platform_tfilter_to_string(tfilter, NULL, 0));
-}
-
-/*****************************************************************************/
-
-void
-nm_platform_cache_update_emit_signal(NMPlatform *     self,
-                                     NMPCacheOpsType  cache_op,
-                                     const NMPObject *obj_old,
-                                     const NMPObject *obj_new)
-{
-    gboolean         visible_new;
-    gboolean         visible_old;
-    const NMPObject *o;
-    const NMPClass * klass;
-    int              ifindex;
-
-    nm_assert(NM_IN_SET((NMPlatformSignalChangeType) cache_op,
-                        NM_PLATFORM_SIGNAL_NONE,
-                        NM_PLATFORM_SIGNAL_ADDED,
-                        NM_PLATFORM_SIGNAL_CHANGED,
-                        NM_PLATFORM_SIGNAL_REMOVED));
-
-    ASSERT_nmp_cache_ops(nm_platform_get_cache(self), cache_op, obj_old, obj_new);
-
-    NMTST_ASSERT_PLATFORM_NETNS_CURRENT(self);
-
-    switch (cache_op) {
-    case NMP_CACHE_OPS_ADDED:
-        if (!nmp_object_is_visible(obj_new))
-            return;
-        o = obj_new;
-        break;
-    case NMP_CACHE_OPS_UPDATED:
-        visible_old = nmp_object_is_visible(obj_old);
-        visible_new = nmp_object_is_visible(obj_new);
-        if (!visible_old && visible_new) {
-            o        = obj_new;
-            cache_op = NMP_CACHE_OPS_ADDED;
-        } else if (visible_old && !visible_new) {
-            o        = obj_old;
-            cache_op = NMP_CACHE_OPS_REMOVED;
-        } else if (!visible_new) {
-            /* it was invisible and stayed invisible. Nothing to do. */
-            return;
-        } else
-            o = obj_new;
-        break;
-    case NMP_CACHE_OPS_REMOVED:
-        if (!nmp_object_is_visible(obj_old))
-            return;
-        o = obj_old;
-        break;
-    default:
-        nm_assert(cache_op == NMP_CACHE_OPS_UNCHANGED);
-        return;
-    }
-
-    klass = NMP_OBJECT_GET_CLASS(o);
-
-    if (klass->obj_type == NMP_OBJECT_TYPE_ROUTING_RULE)
-        ifindex = 0;
-    else
-        ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(o)->ifindex;
-
-    if (klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE
-        && NM_PLATFORM_GET_PRIVATE(self)->ip4_dev_route_blacklist_gc_timeout_id
-        && NM_IN_SET(cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED))
-        _ip4_dev_route_blacklist_notify_route(self, o);
-
-    _LOG3t("emit signal %s %s: %s",
-           klass->signal_type,
-           nm_platform_signal_change_type_to_string((NMPlatformSignalChangeType) cache_op),
-           nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-
-    nmp_object_ref(o);
-    g_signal_emit(self,
-                  _nm_platform_signal_id_get(klass->signal_type_id),
-                  0,
-                  (int) klass->obj_type,
-                  ifindex,
-                  &o->object,
-                  (int) cache_op);
-    nmp_object_unref(o);
-}
-
-/*****************************************************************************/
-
-NMPCache *
-nm_platform_get_cache(NMPlatform *self)
-{
-    return NM_PLATFORM_GET_PRIVATE(self)->cache;
-}
-
-NMPNetns *
-nm_platform_netns_get(NMPlatform *self)
-{
-    _CHECK_SELF(self, klass, NULL);
-
-    return self->_netns;
-}
-
-gboolean
-nm_platform_netns_push(NMPlatform *self, NMPNetns **netns)
-{
-    g_return_val_if_fail(NM_IS_PLATFORM(self), FALSE);
-
-    if (self->_netns && !nmp_netns_push(self->_netns)) {
-        NM_SET_OUT(netns, NULL);
-        return FALSE;
-    }
-
-    NM_SET_OUT(netns, self->_netns);
-    return TRUE;
-}
-
-/*****************************************************************************/
-
-const _NMPlatformVTableRouteUnion nm_platform_vtable_route = {
-    .v4 =
-        {
-            .is_ip4          = TRUE,
-            .obj_type        = NMP_OBJECT_TYPE_IP4_ROUTE,
-            .addr_family     = AF_INET,
-            .sizeof_route    = sizeof(NMPlatformIP4Route),
-            .route_cmp       = (int (*)(const NMPlatformIPXRoute *a,
-                                  const NMPlatformIPXRoute *b,
-                                  NMPlatformIPRouteCmpType  cmp_type)) nm_platform_ip4_route_cmp,
-            .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route,
-                                                 char *                    buf,
-                                                 gsize len)) nm_platform_ip4_route_to_string,
-        },
-    .v6 =
-        {
-            .is_ip4          = FALSE,
-            .obj_type        = NMP_OBJECT_TYPE_IP6_ROUTE,
-            .addr_family     = AF_INET6,
-            .sizeof_route    = sizeof(NMPlatformIP6Route),
-            .route_cmp       = (int (*)(const NMPlatformIPXRoute *a,
-                                  const NMPlatformIPXRoute *b,
-                                  NMPlatformIPRouteCmpType  cmp_type)) nm_platform_ip6_route_cmp,
-            .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route,
-                                                 char *                    buf,
-                                                 gsize len)) nm_platform_ip6_route_to_string,
-        },
-};
-
-/*****************************************************************************/
-
-static void
-set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-    NMPlatform *       self = NM_PLATFORM(object);
-    NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
-
-    switch (prop_id) {
-    case PROP_NETNS_SUPPORT:
-        /* construct-only */
-        if (g_value_get_boolean(value)) {
-            NMPNetns *netns;
-
-            netns = nmp_netns_get_current();
-            if (netns)
-                self->_netns = g_object_ref(netns);
-        }
-        break;
-    case PROP_USE_UDEV:
-        /* construct-only */
-        priv->use_udev = g_value_get_boolean(value);
-        break;
-    case PROP_LOG_WITH_PTR:
-        /* construct-only */
-        priv->log_with_ptr = g_value_get_boolean(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
-nm_platform_init(NMPlatform *self)
-{
-    self->_priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_PLATFORM, NMPlatformPrivate);
-}
-
-static GObject *
-constructor(GType type, guint n_construct_params, GObjectConstructParam *construct_params)
-{
-    GObject *          object;
-    NMPlatform *       self;
-    NMPlatformPrivate *priv;
-
-    object = G_OBJECT_CLASS(nm_platform_parent_class)
-                 ->constructor(type, n_construct_params, construct_params);
-    self = NM_PLATFORM(object);
-    priv = NM_PLATFORM_GET_PRIVATE(self);
-
-    priv->multi_idx = nm_dedup_multi_index_new();
-
-    priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev);
-
-    return object;
-}
-
-static void
-finalize(GObject *object)
-{
-    NMPlatform *       self = NM_PLATFORM(object);
-    NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
-
-    nm_clear_g_source(&priv->ip4_dev_route_blacklist_check_id);
-    nm_clear_g_source(&priv->ip4_dev_route_blacklist_gc_timeout_id);
-    nm_clear_pointer(&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref);
-    g_clear_object(&self->_netns);
-    nm_dedup_multi_index_unref(priv->multi_idx);
-    nmp_cache_free(priv->cache);
-}
-
-static void
-nm_platform_class_init(NMPlatformClass *platform_class)
-{
-    GObjectClass *object_class = G_OBJECT_CLASS(platform_class);
-
-    g_type_class_add_private(object_class, sizeof(NMPlatformPrivate));
-
-    object_class->constructor  = constructor;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
-
-    platform_class->wifi_set_powersave = wifi_set_powersave;
-
-    g_object_class_install_property(
-        object_class,
-        PROP_NETNS_SUPPORT,
-        g_param_spec_boolean(NM_PLATFORM_NETNS_SUPPORT,
-                             "",
-                             "",
-                             NM_PLATFORM_NETNS_SUPPORT_DEFAULT,
-                             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-
-    g_object_class_install_property(
-        object_class,
-        PROP_USE_UDEV,
-        g_param_spec_boolean(NM_PLATFORM_USE_UDEV,
-                             "",
-                             "",
-                             FALSE,
-                             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-
-    g_object_class_install_property(
-        object_class,
-        PROP_LOG_WITH_PTR,
-        g_param_spec_boolean(NM_PLATFORM_LOG_WITH_PTR,
-                             "",
-                             "",
-                             TRUE,
-                             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-
-#define SIGNAL(signal, signal_id, method)                                                \
-    G_STMT_START                                                                         \
-    {                                                                                    \
-        signals[signal] =                                                                \
-            g_signal_new_class_handler("" signal_id "",                                  \
-                                       G_OBJECT_CLASS_TYPE(object_class),                \
-                                       G_SIGNAL_RUN_FIRST,                               \
-                                       G_CALLBACK(method),                               \
-                                       NULL,                                             \
-                                       NULL,                                             \
-                                       NULL,                                             \
-                                       G_TYPE_NONE,                                      \
-                                       4,                                                \
-                                       G_TYPE_INT, /* (int) NMPObjectType */             \
-                                       G_TYPE_INT, /* ifindex */                         \
-                                       G_TYPE_POINTER /* const NMPObject * */,           \
-                                       G_TYPE_INT /* (int) NMPlatformSignalChangeType */ \
-            );                                                                           \
-    }                                                                                    \
-    G_STMT_END
-
-    /* Signals */
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link);
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS,
-           NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED,
-           log_ip4_address);
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS,
-           NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
-           log_ip6_address);
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route);
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route);
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_ROUTING_RULE,
-           NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED,
-           log_routing_rule);
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc);
-    SIGNAL(NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter);
-}
diff --git a/src/core/platform/nm-platform.h b/src/core/platform/nm-platform.h
deleted file mode 100644
index d400c568..00000000
--- a/src/core/platform/nm-platform.h
+++ /dev/null
@@ -1,2372 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2009 - 2018 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_PLATFORM_H__
-#define __NETWORKMANAGER_PLATFORM_H__
-
-#include "nm-dbus-interface.h"
-#include "nm-core-types-internal.h"
-
-#include "nm-platform/nmp-base.h"
-#include "nm-base/nm-base.h"
-
-#include "nm-core-utils.h"
-#include "nm-setting-vlan.h"
-#include "nm-setting-wired.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-ip-tunnel.h"
-
-#define NM_TYPE_PLATFORM (nm_platform_get_type())
-#define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform))
-#define NM_PLATFORM_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PLATFORM, NMPlatformClass))
-#define NM_IS_PLATFORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PLATFORM))
-#define NM_IS_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_PLATFORM))
-#define NM_PLATFORM_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_PLATFORM, NMPlatformClass))
-
-#define NM_PLATFORM_NETNS_SUPPORT_DEFAULT FALSE
-
-/*****************************************************************************/
-
-#define NM_PLATFORM_NETNS_SUPPORT "netns-support"
-#define NM_PLATFORM_USE_UDEV      "use-udev"
-#define NM_PLATFORM_LOG_WITH_PTR  "log-with-ptr"
-
-/*****************************************************************************/
-
-/* IFNAMSIZ is both defined in <linux/if.h> and <net/if.h>. In the past, these
- * headers conflicted, so we cannot simply include either of them in a header-file.*/
-#define NMP_IFNAMSIZ 16
-
-/*****************************************************************************/
-
-struct _NMPWireGuardPeer;
-
-struct udev_device;
-
-typedef gboolean (*NMPObjectPredicateFunc)(const NMPObject *obj, gpointer user_data);
-
-/* workaround for older libnl version, that does not define these flags. */
-#ifndef IFA_F_MANAGETEMPADDR
-    #define IFA_F_MANAGETEMPADDR 0x100
-#endif
-#ifndef IFA_F_NOPREFIXROUTE
-    #define IFA_F_NOPREFIXROUTE 0x200
-#endif
-
-#define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */
-
-/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers
- * that don't define it. */
-#define NM_IN6_ADDR_GEN_MODE_UNKNOWN        255 /* no corresponding value.  */
-#define NM_IN6_ADDR_GEN_MODE_EUI64          0 /* IN6_ADDR_GEN_MODE_EUI64 */
-#define NM_IN6_ADDR_GEN_MODE_NONE           1 /* IN6_ADDR_GEN_MODE_NONE */
-#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */
-#define NM_IN6_ADDR_GEN_MODE_RANDOM         3 /* IN6_ADDR_GEN_MODE_RANDOM */
-
-#define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */
-
-/* Redefine this in host's endianness */
-#define NM_GRE_KEY 0x2000
-
-typedef enum {
-    NMP_NLM_FLAG_F_ECHO = 0x08, /* NLM_F_ECHO, Echo this request */
-
-    /* use our own platform enum for the nlmsg-flags. Otherwise, we'd have
-     * to include <linux/netlink.h> */
-    NMP_NLM_FLAG_F_REPLACE = 0x100, /* NLM_F_REPLACE, Override existing */
-    NMP_NLM_FLAG_F_EXCL    = 0x200, /* NLM_F_EXCL, Do not touch, if it exists */
-    NMP_NLM_FLAG_F_CREATE  = 0x400, /* NLM_F_CREATE, Create, if it does not exist */
-    NMP_NLM_FLAG_F_APPEND  = 0x800, /* NLM_F_APPEND, Add to end of list */
-
-    NMP_NLM_FLAG_FMASK = 0xFFFF, /* a mask for all NMP_NLM_FLAG_F_* flags */
-
-    /* instructs NM to suppress logging an error message for any failures
-     * received from kernel.
-     *
-     * It will still log with debug-level, and it will still log
-     * other failures aside the kernel response. */
-    NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE = 0x10000,
-
-    /* the following aliases correspond to iproute2's `ip route CMD` for
-     * RTM_NEWROUTE, with CMD being one of add, change, replace, prepend,
-     * append and test. */
-    NMP_NLM_FLAG_ADD     = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_EXCL,
-    NMP_NLM_FLAG_CHANGE  = NMP_NLM_FLAG_F_REPLACE,
-    NMP_NLM_FLAG_REPLACE = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_REPLACE,
-    NMP_NLM_FLAG_PREPEND = NMP_NLM_FLAG_F_CREATE,
-    NMP_NLM_FLAG_APPEND  = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_APPEND,
-    NMP_NLM_FLAG_TEST    = NMP_NLM_FLAG_F_EXCL,
-} NMPNlmFlags;
-
-typedef enum {
-    /* compare fields which kernel considers as similar routes.
-     * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
-     * and means that `ip route add` would fail to add two routes
-     * that have the same NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID.
-     * On the other hand, `ip route append` would allow that, as
-     * long as NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID differs. */
-    NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID,
-
-    /* compare two routes as kernel would allow to add them with
-     * `ip route append`. In other words, kernel does not allow you to
-     * add two routes (at the same time) which compare equal according
-     * to NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID.
-     *
-     * For the ID we can only recognize route fields that we actually implement.
-     * However, kernel supports more routing options, some of them also part of
-     * the ID. NetworkManager is oblivious to these options and will wrongly think
-     * that two routes are identical, while they are not. That can lead to an
-     * inconsistent platform cache. Not much what we can do about that, except
-     * implementing all options that kernel supports *sigh*. See rh#1337860.
-     */
-    NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID,
-
-    /* compare all fields as they make sense for kernel. For example,
-     * a route destination 192.168.1.5/24 is not accepted by kernel and
-     * we treat it identical to 192.168.1.0/24. Semantically these
-     * routes are identical, but NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL will
-     * report them as different.
-     *
-     * The result shall be identical to call first nm_platform_ip_route_normalize()
-     * on both routes and then doing a full comparison. */
-    NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY,
-
-    /* compare all fields. This should have the same effect as memcmp(),
-     * except allowing for undefined data in holes between field alignment.
-     */
-    NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL,
-
-} NMPlatformIPRouteCmpType;
-
-typedef enum {
-    NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID,
-
-    NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY,
-
-    NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL,
-} NMPlatformRoutingRuleCmpType;
-
-typedef struct {
-    union {
-        guint8      data[20 /* NM_UTILS_HWADDR_LEN_MAX */];
-        NMEtherAddr ether_addr;
-    };
-    guint8 len;
-} NMPLinkAddress;
-
-/* assert that NMEtherAddr does not affect the alignment of NMPLinkAddress struct. */
-G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) == 1);
-G_STATIC_ASSERT(_nm_alignof(NMPLinkAddress) == 1);
-
-gconstpointer nmp_link_address_get(const NMPLinkAddress *addr, size_t *length);
-GBytes *      nmp_link_address_get_as_bytes(const NMPLinkAddress *addr);
-
-typedef enum {
-
-    /* match-flags are strictly inclusive. That means,
-     * by default nothing is matched, but if you enable a particular
-     * flag, a candidate that matches passes the check.
-     *
-     * In other words: adding more flags can only extend the result
-     * set of matching objects.
-     *
-     * Also, the flags form partitions. Like, an address can be either of
-     * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for
-     * the ADDRSTATE match types.
-     */
-    NM_PLATFORM_MATCH_WITH_NONE = 0,
-
-    NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL    = (1LL << 0),
-    NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1),
-    NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY =
-        NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL,
-
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL    = (1LL << 2),
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE = (1LL << 3),
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED = (1LL << 4),
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY      = NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL
-                                            | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE
-                                            | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED,
-} NMPlatformMatchFlags;
-
-#define NM_PLATFORM_LINK_OTHER_NETNS (-1)
-
-struct _NMPlatformObject {
-    /* the object type has no fields of its own, it is only used to having
-     * a special pointer type that can be used to indicate "any" type. */
-    char _dummy_don_t_use_me;
-};
-
-#define __NMPlatformObjWithIfindex_COMMON \
-    int ifindex;                          \
-    ;
-
-struct _NMPlatformObjWithIfindex {
-    __NMPlatformObjWithIfindex_COMMON;
-};
-
-struct _NMPlatformLink {
-    __NMPlatformObjWithIfindex_COMMON;
-    char       name[NMP_IFNAMSIZ];
-    NMLinkType type;
-
-    /* rtnl_link_get_type(), IFLA_INFO_KIND. */
-    /* NMPlatform initializes this field with a static string. */
-    const char *kind;
-
-    /* NMPlatform initializes this field with a static string. */
-    const char *driver;
-
-    int master;
-
-    /* rtnl_link_get_link(), IFLA_LINK.
-     * If IFLA_LINK_NETNSID indicates that the parent is in another namespace,
-     * this field be set to (negative) NM_PLATFORM_LINK_OTHER_NETNS. */
-    int parent;
-
-    /* IFF_* flags. Note that the flags in 'struct ifinfomsg' are declared as 'unsigned'. */
-    guint n_ifi_flags;
-
-    guint mtu;
-
-    /* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */
-    guint32 arptype;
-
-    /* IFLA_ADDRESS */
-    NMPLinkAddress l_address;
-
-    /* IFLA_BROADCAST */
-    NMPLinkAddress l_broadcast;
-
-    /* rtnl_link_inet6_get_token(), IFLA_INET6_TOKEN */
-    NMUtilsIPv6IfaceId inet6_token;
-
-    /* The bitwise inverse of rtnl_link_inet6_get_addr_gen_mode(). It is inverse
-     * to have a default of 0 -- meaning: unspecified. That way, a struct
-     * initialized with memset(0) has and unset value.*/
-    guint8 inet6_addr_gen_mode_inv;
-
-    /* Statistics */
-    guint64 rx_packets;
-    guint64 rx_bytes;
-    guint64 tx_packets;
-    guint64 tx_bytes;
-
-    /* @connected is mostly identical to (@n_ifi_flags & IFF_UP). Except for bridge/bond masters,
-     * where we coerce the link as disconnect if it has no slaves. */
-    bool connected : 1;
-
-    bool initialized : 1;
-};
-
-typedef enum { /*< skip >*/
-               NM_PLATFORM_SIGNAL_ID_NONE,
-               NM_PLATFORM_SIGNAL_ID_LINK,
-               NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS,
-               NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS,
-               NM_PLATFORM_SIGNAL_ID_IP4_ROUTE,
-               NM_PLATFORM_SIGNAL_ID_IP6_ROUTE,
-               NM_PLATFORM_SIGNAL_ID_ROUTING_RULE,
-               NM_PLATFORM_SIGNAL_ID_QDISC,
-               NM_PLATFORM_SIGNAL_ID_TFILTER,
-               _NM_PLATFORM_SIGNAL_ID_LAST,
-} NMPlatformSignalIdType;
-
-guint _nm_platform_signal_id_get(NMPlatformSignalIdType signal_type);
-
-typedef enum {
-    NM_PLATFORM_SIGNAL_NONE,
-    NM_PLATFORM_SIGNAL_ADDED,
-    NM_PLATFORM_SIGNAL_CHANGED,
-    NM_PLATFORM_SIGNAL_REMOVED,
-} NMPlatformSignalChangeType;
-
-#define NM_PLATFORM_IP_ADDRESS_CAST(address) \
-    NM_CONSTCAST(NMPlatformIPAddress,        \
-                 (address),                  \
-                 NMPlatformIPXAddress,       \
-                 NMPlatformIP4Address,       \
-                 NMPlatformIP6Address)
-
-#define __NMPlatformIPAddress_COMMON                                                         \
-    __NMPlatformObjWithIfindex_COMMON;                                                       \
-    NMIPConfigSource addr_source;                                                            \
-                                                                                             \
-    /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*().
-     *
-     * The rules are:
-     * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses
-     *   are permanent. This rule is so that unset addresses (calloc) are permanent by default.
-     * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly
-     *   set to 0). Such addresses are permanent.
-     * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp
-     *   and never returned by nm_utils_get_monotonic_timestamp_sec(). In this case @valid/@preferred
-     *   is anchored at @timestamp.
-     * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time
-     *   moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add().
-     *
-     * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the
-     * lease was received. Addresses from kernel might have the @timestamp based on the last modification
-     * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring
-     * @lifetime and @preferred.
-     */ \
-    guint32 timestamp;                                                                       \
-    guint32 lifetime;  /* seconds since timestamp */                                         \
-    guint32 preferred; /* seconds since timestamp */                                         \
-                                                                                             \
-    /* ifa_flags in 'struct ifaddrmsg' from <linux/if_addr.h>, extended to 32 bit by
-     * IFA_FLAGS attribute. */         \
-    guint32 n_ifa_flags;                                                                     \
-                                                                                             \
-    guint8 plen;                                                                             \
-                                                                                             \
-    /* FIXME(l3cfg): the external marker won't be necessary anymore, because we only
-     * merge addresses we care about, and ignore (don't remove) external addresses. */         \
-    bool external : 1;                                                                       \
-                                                                                             \
-    bool use_ip4_broadcast_address : 1;                                                      \
-                                                                                             \
-    /* Whether the address is ready to be configured. By default, an address is, but this
-     * flag may indicate that the address is just for tracking purpose only, but the ACD
-     * state is not yet ready for the address to be configured. */    \
-    bool ip4acd_not_ready : 1;                                                               \
-    ;
-
-/**
- * NMPlatformIPAddress:
- *
- * Common parts of NMPlatformIP4Address and NMPlatformIP6Address.
- **/
-typedef struct {
-    __NMPlatformIPAddress_COMMON;
-    union {
-        guint8  address_ptr[1];
-        guint32 __dummy_for_32bit_alignment;
-    };
-} NMPlatformIPAddress;
-
-/**
- * NMPlatformIP4Address:
- * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec()
- **/
-struct _NMPlatformIP4Address {
-    __NMPlatformIPAddress_COMMON;
-
-    /* The local address IFA_LOCAL. */
-    in_addr_t address;
-
-    /* The IFA_ADDRESS PTP peer address. This field is rather important, because
-     * it constitutes the identifier for the IPv4 address (e.g. you can add two
-     * addresses that only differ by their peer's network-part.
-     *
-     * Beware that for most cases, NetworkManager doesn't want to set an explicit
-     * peer-address. However, that corresponds to setting the peer address to @address
-     * itself. Leaving peer-address unset/zero, means explicitly setting the peer
-     * address to 0.0.0.0, which you probably don't want.
-     * */
-    in_addr_t peer_address; /* PTP peer address */
-
-    /* IFA_BROADCAST.
-     *
-     * This parameter is ignored unless use_ip4_broadcast_address is TRUE.
-     * See nm_platform_ip4_broadcast_address_from_addr(). */
-    in_addr_t broadcast_address;
-
-    char label[NMP_IFNAMSIZ];
-};
-
-/**
- * NMPlatformIP6Address:
- * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec()
- **/
-struct _NMPlatformIP6Address {
-    __NMPlatformIPAddress_COMMON;
-    struct in6_addr address;
-    struct in6_addr peer_address;
-};
-
-typedef union {
-    NMPlatformIPAddress  ax;
-    NMPlatformIP4Address a4;
-    NMPlatformIP6Address a6;
-} NMPlatformIPXAddress;
-
-#undef __NMPlatformIPAddress_COMMON
-
-#define NM_PLATFORM_IP4_ADDRESS_INIT(...) (&((const NMPlatformIP4Address){__VA_ARGS__}))
-
-#define NM_PLATFORM_IP6_ADDRESS_INIT(...) (&((const NMPlatformIP6Address){__VA_ARGS__}))
-
-/* Default value for adding an IPv4 route. This is also what iproute2 does.
- * Note that contrary to IPv6, you can add routes with metric 0 and it is even
- * the default.
- */
-#define NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4 ((guint32) 0u)
-
-/* Default value for adding an IPv6 route. This is also what iproute2 does.
- * Adding an IPv6 route with metric 0, kernel translates to IP6_RT_PRIO_USER (1024).
- *
- * Note that kernel doesn't allow adding IPv6 routes with metric zero via netlink.
- * It however can itself add routes with metric zero. */
-#define NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6 ((guint32) 1024u)
-
-/* For IPv4, kernel adds a device route (subnet routes) with metric 0 when user
- * configures addresses. */
-#define NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE ((guint32) 0u)
-
-#define __NMPlatformIPRoute_COMMON                                                        \
-    __NMPlatformObjWithIfindex_COMMON;                                                    \
-                                                                                          \
-    /* The NMIPConfigSource. For routes that we receive from cache this corresponds
-     * to the rtm_protocol field (and is one of the NM_IP_CONFIG_SOURCE_RTPROT_* values).
-     * When adding a route, the source will be coerced to the protocol using
-     * nmp_utils_ip_config_source_coerce_to_rtprot().
-     *
-     * rtm_protocol is part of the primary key of an IPv4 route (meaning, you can add
-     * two IPv4 routes that only differ in their rtm_protocol. For IPv6, that is not
-     * the case.
-     *
-     * When deleting an IPv4/IPv6 route, the rtm_protocol field must match (even
-     * if it is not part of the primary key for IPv6) -- unless rtm_protocol is set
-     * to zero, in which case the first matching route (with proto ignored) is deleted. */       \
-    NMIPConfigSource rt_source;                                                           \
-                                                                                          \
-    guint8 plen;                                                                          \
-                                                                                          \
-    /* RTA_METRICS:
-     *
-     * For IPv4 routes, these properties are part of their
-     * ID (meaning: you can add otherwise identical IPv4 routes that
-     * only differ by the metric property).
-     * On the other hand, for IPv6 you cannot add two IPv6 routes that only differ
-     * by an RTA_METRICS property.
-     *
-     * When deleting a route, kernel seems to ignore the RTA_METRICS properties.
-     * That is a problem/bug for IPv4 because you cannot explicitly select which
-     * route to delete. Kernel just picks the first. See rh#1475642. */                                                                       \
-                                                                                          \
-    /* RTA_METRICS.RTAX_LOCK (iproute2: "lock" arguments) */                              \
-    bool lock_window : 1;                                                                 \
-    bool lock_cwnd : 1;                                                                   \
-    bool lock_initcwnd : 1;                                                               \
-    bool lock_initrwnd : 1;                                                               \
-    bool lock_mtu : 1;                                                                    \
-                                                                                          \
-    /* if TRUE, the "metric" field is interpreted as an offset that is added to a default
-     * metric. For example, form a DHCP lease we don't know the actually used metric, because
-     * that is determined by upper layers (the configuration). However, we have a default
-     * metric that should be used. So we set "metric_any" to %TRUE, which means to use
-     * the default metric. However, we still treat the "metric" field as an offset that
-     * will be added to the default metric. In most case, you want that "metric" is zero
-     * when setting "metric_any". */ \
-    bool metric_any : 1;                                                                  \
-                                                                                          \
-    /* like "metric_any", the table is determined by other layers of the code.
-     * This field overrides "table_coerced" field. If "table_any" is true, then
-     * the "table_coerced" field is ignored (unlike for the metric). */            \
-    bool table_any : 1;                                                                   \
-                                                                                          \
-    /* rtnh_flags
-     *
-     * Routes with rtm_flags RTM_F_CLONED are hidden by platform and
-     * do not exist from the point-of-view of platform users.
-     * Such a route is not alive, according to nmp_object_is_alive().
-     *
-     * NOTE: currently we ignore all flags except RTM_F_CLONED
-     * and RTNH_F_ONLINK.
-     * We also may not properly consider the flags as part of the ID
-     * in route-cmp. */                                                                         \
-    unsigned r_rtm_flags;                                                                 \
-                                                                                          \
-    /* RTA_METRICS.RTAX_ADVMSS (iproute2: advmss) */                                      \
-    guint32 mss;                                                                          \
-                                                                                          \
-    /* RTA_METRICS.RTAX_WINDOW (iproute2: window) */                                      \
-    guint32 window;                                                                       \
-                                                                                          \
-    /* RTA_METRICS.RTAX_CWND (iproute2: cwnd) */                                          \
-    guint32 cwnd;                                                                         \
-                                                                                          \
-    /* RTA_METRICS.RTAX_INITCWND (iproute2: initcwnd) */                                  \
-    guint32 initcwnd;                                                                     \
-                                                                                          \
-    /* RTA_METRICS.RTAX_INITRWND (iproute2: initrwnd) */                                  \
-    guint32 initrwnd;                                                                     \
-                                                                                          \
-    /* RTA_METRICS.RTAX_MTU (iproute2: mtu) */                                            \
-    guint32 mtu;                                                                          \
-                                                                                          \
-    /* RTA_PRIORITY (iproute2: metric)
-     * If "metric_any" is %TRUE, then this is interpreted as an offset that will be
-     * added to a default base metric. In such cases, the offset is usually zero. */                                                    \
-    guint32 metric;                                                                       \
-                                                                                          \
-    /* rtm_table, RTA_TABLE.
-     *
-     * This is not the original table ID. Instead, 254 (RT_TABLE_MAIN) and
-     * zero (RT_TABLE_UNSPEC) are swapped, so that the default is the main
-     * table. Use nm_platform_route_table_coerce()/nm_platform_route_table_uncoerce(). */                                                              \
-    guint32 table_coerced;                                                                \
-                                                                                          \
-    /* rtm_type.
-     *
-     * This is not the original type, if type_coerced is 0 then
-     * it means RTN_UNSPEC otherwise the type value is preserved.
-     * */                                                                          \
-    guint8 type_coerced;                                                                  \
-                                                                                          \
-    /*end*/
-
-typedef struct {
-    __NMPlatformIPRoute_COMMON;
-    union {
-        guint8  network_ptr[1];
-        guint32 __dummy_for_32bit_alignment;
-    };
-} NMPlatformIPRoute;
-
-#define NM_PLATFORM_IP_ROUTE_CAST(route) \
-    NM_CONSTCAST(NMPlatformIPRoute,      \
-                 (route),                \
-                 NMPlatformIPXRoute,     \
-                 NMPlatformIP4Route,     \
-                 NMPlatformIP6Route)
-
-#define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) (NM_PLATFORM_IP_ROUTE_CAST(route)->plen <= 0)
-
-struct _NMPlatformIP4Route {
-    __NMPlatformIPRoute_COMMON;
-    in_addr_t network;
-
-    /* RTA_GATEWAY. The gateway is part of the primary key for a route */
-    in_addr_t gateway;
-
-    /* RTA_PREFSRC (called "src" by iproute2).
-     *
-     * pref_src is part of the ID of an IPv4 route. When deleting a route,
-     * pref_src must match, unless set to 0.0.0.0 to match any. */
-    in_addr_t pref_src;
-
-    /* rtm_tos (iproute2: tos)
-     *
-     * For IPv4, tos is part of the weak-id (like metric).
-     *
-     * For IPv6, tos is ignored by kernel.  */
-    guint8 tos;
-
-    /* The bitwise inverse of the route scope rtm_scope. It is inverted so that the
-     * default value (RT_SCOPE_NOWHERE) is zero. Use nm_platform_route_scope_inv()
-     * to convert back and forth between the inverse representation and the
-     * real value.
-     *
-     * rtm_scope is part of the primary key for IPv4 routes. When deleting a route,
-     * the scope must match, unless it is left at RT_SCOPE_NOWHERE, in which case the first
-     * matching route is deleted.
-     *
-     * For IPv6 routes, the scope is ignored and kernel always assumes global scope.
-     * Hence, this field is only in NMPlatformIP4Route. */
-    guint8 scope_inv;
-};
-
-struct _NMPlatformIP6Route {
-    __NMPlatformIPRoute_COMMON;
-    struct in6_addr network;
-
-    /* RTA_GATEWAY. The gateway is part of the primary key for a route */
-    struct in6_addr gateway;
-
-    /* RTA_PREFSRC (called "src" by iproute2).
-     *
-     * pref_src is not part of the ID for an IPv6 route. You cannot add two
-     * routes that only differ by pref_src.
-     *
-     * When deleting a route, pref_src is ignored by kernel. */
-    struct in6_addr pref_src;
-
-    /* RTA_SRC and rtm_src_len (called "from" by iproute2).
-     *
-     * Kernel clears the host part of src/src_plen.
-     *
-     * src/src_plen is part of the ID of a route just like network/plen. That is,
-     * Not only `ip route append`, but also `ip route add` allows to add routes that only
-     * differ in their src/src_plen.
-     */
-    struct in6_addr src;
-    guint8          src_plen;
-
-    /* RTA_PREF router preference.
-     *
-     * The type is guint8 to keep the struct size small. But the values are compatible with
-     * the NMIcmpv6RouterPref enum. */
-    guint8 rt_pref;
-};
-
-typedef union {
-    NMPlatformIPRoute  rx;
-    NMPlatformIP4Route r4;
-    NMPlatformIP6Route r6;
-} NMPlatformIPXRoute;
-
-#undef __NMPlatformIPRoute_COMMON
-
-typedef struct {
-    /* struct fib_rule_uid_range */
-    guint32 start;
-    guint32 end;
-} NMFibRuleUidRange;
-
-typedef struct {
-    /* struct fib_rule_port_range */
-    guint16 start;
-    guint16 end;
-} NMFibRulePortRange;
-
-typedef struct {
-    NMIPAddr           src;                        /* FRA_SRC */
-    NMIPAddr           dst;                        /* FRA_DST */
-    guint64            tun_id;                     /* betoh64(FRA_TUN_ID) */
-    guint32            table;                      /* (struct fib_rule_hdr).table, FRA_TABLE */
-    guint32            flags;                      /* (struct fib_rule_hdr).flags */
-    guint32            priority;                   /* RA_PRIORITY */
-    guint32            fwmark;                     /* FRA_FWMARK */
-    guint32            fwmask;                     /* FRA_FWMASK */
-    guint32            goto_target;                /* FRA_GOTO */
-    guint32            flow;                       /* FRA_FLOW */
-    guint32            suppress_prefixlen_inverse; /* ~(FRA_SUPPRESS_PREFIXLEN) */
-    guint32            suppress_ifgroup_inverse;   /* ~(FRA_SUPPRESS_IFGROUP) */
-    NMFibRuleUidRange  uid_range;                  /* FRA_UID_RANGE */
-    NMFibRulePortRange sport_range;                /* FRA_SPORT_RANGE */
-    NMFibRulePortRange dport_range;                /* FRA_DPORT_RANGE */
-    char               iifname[NMP_IFNAMSIZ];      /* FRA_IIFNAME */
-    char               oifname[NMP_IFNAMSIZ];      /* FRA_OIFNAME */
-    guint8             addr_family;                /* (struct fib_rule_hdr).family */
-    guint8             action;                     /* (struct fib_rule_hdr).action */
-    guint8             tos;                        /* (struct fib_rule_hdr).tos */
-    guint8             src_len;                    /* (struct fib_rule_hdr).src_len */
-    guint8             dst_len;                    /* (struct fib_rule_hdr).dst_len */
-    guint8             l3mdev;                     /* FRA_L3MDEV */
-    guint8             protocol;                   /* FRA_PROTOCOL */
-    guint8             ip_proto;                   /* FRA_IP_PROTO */
-
-    bool uid_range_has : 1; /* has(FRA_UID_RANGE) */
-} NMPlatformRoutingRule;
-
-#define NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET (~((guint32) 0))
-
-#define NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED ((guint32) 0x83126E97u)
-
-G_STATIC_ASSERT(((((guint64) NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) * 1000u) >> 10)
-                == (guint64) INT_MAX);
-
-typedef struct {
-    guint32 limit;
-    guint32 flows;
-    guint32 target;
-    guint32 interval;
-    guint32 quantum;
-
-    /* TCA_FQ_CODEL_CE_THRESHOLD: kernel internally stores this value as
-     * ((val64 * NSEC_PER_USEC) >> CODEL_SHIFT). The default value (in
-     * the domain with this coercion) is CODEL_DISABLED_THRESHOLD (INT_MAX).
-     * That means, "disabled" is expressed on RTM_NEWQDISC netlink API by absence of the
-     * netlink attribute but also as the special value 0x83126E97u
-     * (NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED).
-     * Beware: zero is not the default you must always explicitly set this value. */
-    guint32 ce_threshold;
-
-    /* TCA_FQ_CODEL_MEMORY_LIMIT: note that only values <= 2^31 are accepted by kernel
-     * and kernel defaults to 32MB.
-     * Note that we use the special value NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET
-     * to indicate that no explicit limit is set (when we send a RTM_NEWQDISC request).
-     * This will cause kernel to choose the default (32MB).
-     * Beware: zero is not the default you must always explicitly set this value. */
-    guint32 memory_limit;
-
-    bool ecn : 1;
-} NMPlatformQdiscFqCodel;
-
-typedef struct {
-    unsigned quantum;
-    int      perturb_period;
-    guint32  limit;
-    unsigned divisor;
-    unsigned flows;
-    unsigned depth;
-} NMPlatformQdiscSfq;
-
-typedef struct {
-    guint64 rate;
-    guint32 burst;
-    guint32 limit;
-    guint32 latency;
-} NMPlatformQdiscTbf;
-
-typedef struct {
-    __NMPlatformObjWithIfindex_COMMON;
-
-    /* beware, kind is embedded in an NMPObject, hence you must
-     * take care of the lifetime of the string. */
-    const char *kind;
-
-    int     addr_family;
-    guint32 handle;
-    guint32 parent;
-    guint32 info;
-    union {
-        NMPlatformQdiscFqCodel fq_codel;
-        NMPlatformQdiscSfq     sfq;
-        NMPlatformQdiscTbf     tbf;
-    };
-} NMPlatformQdisc;
-
-typedef struct {
-    char sdata[32];
-} NMPlatformActionSimple;
-
-typedef struct {
-    int  ifindex;
-    bool egress : 1;
-    bool ingress : 1;
-    bool mirror : 1;
-    bool redirect : 1;
-} NMPlatformActionMirred;
-
-typedef struct {
-    /* beware, kind is embedded in an NMPObject, hence you must
-     * take care of the lifetime of the string. */
-    const char *kind;
-
-    union {
-        NMPlatformActionSimple simple;
-        NMPlatformActionMirred mirred;
-    };
-} NMPlatformAction;
-
-#define NM_PLATFORM_ACTION_KIND_SIMPLE "simple"
-#define NM_PLATFORM_ACTION_KIND_MIRRED "mirred"
-
-typedef struct {
-    __NMPlatformObjWithIfindex_COMMON;
-
-    /* beware, kind is embedded in an NMPObject, hence you must
-     * take care of the lifetime of the string. */
-    const char *kind;
-
-    int              addr_family;
-    guint32          handle;
-    guint32          parent;
-    guint32          info;
-    NMPlatformAction action;
-} NMPlatformTfilter;
-
-#undef __NMPlatformObjWithIfindex_COMMON
-
-typedef struct {
-    gboolean      is_ip4;
-    NMPObjectType obj_type;
-    int           addr_family;
-    gsize         sizeof_route;
-    int (*route_cmp)(const NMPlatformIPXRoute *a,
-                     const NMPlatformIPXRoute *b,
-                     NMPlatformIPRouteCmpType  cmp_type);
-    const char *(*route_to_string)(const NMPlatformIPXRoute *route, char *buf, gsize len);
-} NMPlatformVTableRoute;
-
-typedef union {
-    struct {
-        NMPlatformVTableRoute v6;
-        NMPlatformVTableRoute v4;
-    };
-    NMPlatformVTableRoute vx[2];
-} _NMPlatformVTableRouteUnion;
-
-extern const _NMPlatformVTableRouteUnion nm_platform_vtable_route;
-
-typedef struct {
-    guint16 id;
-    guint32 qos;
-    bool    proto_ad : 1;
-} NMPlatformVFVlan;
-
-typedef struct {
-    guint32           index;
-    guint32           min_tx_rate;
-    guint32           max_tx_rate;
-    guint             num_vlans;
-    NMPlatformVFVlan *vlans;
-    struct {
-        guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */
-        guint8 len;
-    } mac;
-    gint8 spoofchk;
-    gint8 trust;
-} NMPlatformVF;
-
-typedef struct {
-    guint16 vid_start;
-    guint16 vid_end;
-    bool    untagged : 1;
-    bool    pvid : 1;
-} NMPlatformBridgeVlan;
-
-typedef struct {
-    NMEtherAddr group_addr;
-    bool        mcast_querier : 1;
-    bool        mcast_query_use_ifaddr : 1;
-    bool        mcast_snooping : 1;
-    bool        stp_state : 1;
-    bool        vlan_stats_enabled : 1;
-    guint16     group_fwd_mask;
-    guint16     priority;
-    guint16     vlan_protocol;
-    guint32     ageing_time;
-    guint32     forward_delay;
-    guint32     hello_time;
-    guint32     max_age;
-    guint32     mcast_last_member_count;
-    guint32     mcast_startup_query_count;
-    guint32     mcast_hash_max;
-    guint64     mcast_last_member_interval;
-    guint64     mcast_membership_interval;
-    guint64     mcast_querier_interval;
-    guint64     mcast_query_interval;
-    guint64     mcast_query_response_interval;
-    guint64     mcast_startup_query_interval;
-    guint8      mcast_router;
-} NMPlatformLnkBridge;
-
-extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default;
-
-typedef struct {
-    in_addr_t local;
-    in_addr_t remote;
-    int       parent_ifindex;
-    guint16   input_flags;
-    guint16   output_flags;
-    guint32   input_key;
-    guint32   output_key;
-    guint8    ttl;
-    guint8    tos;
-    bool      path_mtu_discovery : 1;
-    bool      is_tap : 1;
-} NMPlatformLnkGre;
-
-typedef struct {
-    int         p_key;
-    const char *mode;
-} NMPlatformLnkInfiniband;
-
-typedef struct {
-    struct in6_addr local;
-    struct in6_addr remote;
-    int             parent_ifindex;
-    guint8          ttl;
-    guint8          tclass;
-    guint8          encap_limit;
-    guint8          proto;
-    guint           flow_label;
-    guint32         flags;
-
-    /* IP6GRE only */
-    guint32 input_key;
-    guint32 output_key;
-    guint16 input_flags;
-    guint16 output_flags;
-    bool    is_tap : 1;
-    bool    is_gre : 1;
-} NMPlatformLnkIp6Tnl;
-
-typedef struct {
-    in_addr_t local;
-    in_addr_t remote;
-    int       parent_ifindex;
-    guint8    ttl;
-    guint8    tos;
-    bool      path_mtu_discovery : 1;
-} NMPlatformLnkIpIp;
-
-typedef struct {
-    int     parent_ifindex;
-    guint64 sci; /* host byte order */
-    guint64 cipher_suite;
-    guint32 window;
-    guint8  icv_length;
-    guint8  encoding_sa;
-    guint8  validation;
-    bool    encrypt : 1;
-    bool    protect : 1;
-    bool    include_sci : 1;
-    bool    es : 1;
-    bool    scb : 1;
-    bool    replay_protect : 1;
-} NMPlatformLnkMacsec;
-
-typedef struct {
-    guint mode;
-    bool  no_promisc : 1;
-    bool  tap : 1;
-} NMPlatformLnkMacvlan;
-
-typedef struct {
-    in_addr_t local;
-    in_addr_t remote;
-    int       parent_ifindex;
-    guint16   flags;
-    guint8    ttl;
-    guint8    tos;
-    guint8    proto;
-    bool      path_mtu_discovery : 1;
-} NMPlatformLnkSit;
-
-typedef struct {
-    guint32 owner;
-    guint32 group;
-
-    guint8 type;
-
-    bool owner_valid : 1;
-    bool group_valid : 1;
-
-    bool pi : 1;
-    bool vnet_hdr : 1;
-    bool multi_queue : 1;
-    bool persist : 1;
-} NMPlatformLnkTun;
-
-typedef struct {
-    /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */
-    guint16     id;
-    NMVlanFlags flags;
-} NMPlatformLnkVlan;
-
-typedef struct {
-    guint32 table;
-} NMPlatformLnkVrf;
-
-typedef struct {
-    struct in6_addr group6;
-    struct in6_addr local6;
-    in_addr_t       group;
-    in_addr_t       local;
-    int             parent_ifindex;
-    guint32         id;
-    guint32         ageing;
-    guint32         limit;
-    guint16         dst_port;
-    guint16         src_port_min;
-    guint16         src_port_max;
-    guint8          tos;
-    guint8          ttl;
-    bool            learning : 1;
-    bool            proxy : 1;
-    bool            rsc : 1;
-    bool            l2miss : 1;
-    bool            l3miss : 1;
-} NMPlatformLnkVxlan;
-
-#define NMP_WIREGUARD_PUBLIC_KEY_LEN    32
-#define NMP_WIREGUARD_SYMMETRIC_KEY_LEN 32
-
-typedef struct {
-    guint32 fwmark;
-    guint16 listen_port;
-    guint8  private_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
-    guint8  public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
-} NMPlatformLnkWireGuard;
-
-typedef enum {
-    NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE            = 0,
-    NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS   = (1LL << 0),
-    NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY = (1LL << 1),
-    NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT = (1LL << 2),
-    NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK      = (1LL << 3),
-} NMPlatformWireGuardChangeFlags;
-
-typedef enum {
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE                   = 0,
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME              = (1LL << 0),
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY      = (1LL << 1),
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL = (1LL << 2),
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT           = (1LL << 3),
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS         = (1LL << 4),
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS     = (1LL << 5),
-
-    NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT =
-        NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY
-        | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL
-        | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT
-        | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS,
-
-} NMPlatformWireGuardChangePeerFlags;
-
-typedef void (*NMPlatformAsyncCallback)(GError *error, gpointer user_data);
-
-/*****************************************************************************/
-
-typedef enum {
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS,
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL,
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF,
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV,
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE,
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL,
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED,
-
-    /* this also includes FRA_SPORT_RANGE and FRA_DPORT_RANGE which
-     * were added at the same time. */
-    NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO,
-
-    _NM_PLATFORM_KERNEL_SUPPORT_NUM,
-} NMPlatformKernelSupportType;
-
-extern volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPORT_NUM];
-
-int _nm_platform_kernel_support_init(NMPlatformKernelSupportType type, int value);
-
-static inline gboolean
-_nm_platform_kernel_support_detected(NMPlatformKernelSupportType type)
-{
-    nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < G_N_ELEMENTS(_nm_platform_kernel_support_state));
-
-    return G_LIKELY(g_atomic_int_get(&_nm_platform_kernel_support_state[type]) != 0);
-}
-
-static inline NMOptionBool
-nm_platform_kernel_support_get_full(NMPlatformKernelSupportType type, gboolean init_if_not_set)
-{
-    int v;
-
-    nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < G_N_ELEMENTS(_nm_platform_kernel_support_state));
-
-    v = g_atomic_int_get(&_nm_platform_kernel_support_state[type]);
-    if (G_UNLIKELY(v == 0)) {
-        if (!init_if_not_set)
-            return NM_OPTION_BOOL_DEFAULT;
-        v = _nm_platform_kernel_support_init(type, 0);
-    }
-    return (v >= 0);
-}
-
-static inline gboolean
-nm_platform_kernel_support_get(NMPlatformKernelSupportType type)
-{
-    return nm_platform_kernel_support_get_full(type, TRUE) != NM_OPTION_BOOL_FALSE;
-}
-
-/*****************************************************************************/
-
-struct _NMPlatformPrivate;
-
-struct _NMPlatform {
-    GObject                    parent;
-    NMPNetns *                 _netns;
-    struct _NMPlatformPrivate *_priv;
-};
-
-typedef struct {
-    GObjectClass parent;
-
-    gboolean (*sysctl_set)(NMPlatform *self,
-                           const char *pathid,
-                           int         dirfd,
-                           const char *path,
-                           const char *value);
-    void (*sysctl_set_async)(NMPlatform *            self,
-                             const char *            pathid,
-                             int                     dirfd,
-                             const char *            path,
-                             const char *const *     values,
-                             NMPlatformAsyncCallback callback,
-                             gpointer                data,
-                             GCancellable *          cancellable);
-    char *(*sysctl_get)(NMPlatform *self, const char *pathid, int dirfd, const char *path);
-
-    void (*refresh_all)(NMPlatform *self, NMPObjectType obj_type);
-    void (*process_events)(NMPlatform *self);
-
-    int (*link_add)(NMPlatform *           self,
-                    NMLinkType             type,
-                    const char *           name,
-                    int                    parent,
-                    const void *           address,
-                    size_t                 address_len,
-                    guint32                mtu,
-                    gconstpointer          extra_data,
-                    const NMPlatformLink **out_link);
-    gboolean (*link_delete)(NMPlatform *self, int ifindex);
-    gboolean (*link_refresh)(NMPlatform *self, int ifindex);
-    gboolean (*link_set_netns)(NMPlatform *self, int ifindex, int netns_fd);
-    gboolean (*link_set_up)(NMPlatform *self, int ifindex, gboolean *out_no_firmware);
-    gboolean (*link_set_down)(NMPlatform *self, int ifindex);
-    gboolean (*link_set_arp)(NMPlatform *self, int ifindex);
-    gboolean (*link_set_noarp)(NMPlatform *self, int ifindex);
-
-    int (*link_set_user_ipv6ll_enabled)(NMPlatform *self, int ifindex, gboolean enabled);
-    gboolean (*link_set_token)(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid);
-
-    gboolean (*link_get_permanent_address)(NMPlatform *self,
-                                           int         ifindex,
-                                           guint8 *    buf,
-                                           size_t *    length);
-    int (*link_set_address)(NMPlatform *self, int ifindex, gconstpointer address, size_t length);
-    int (*link_set_mtu)(NMPlatform *self, int ifindex, guint32 mtu);
-    gboolean (*link_set_name)(NMPlatform *self, int ifindex, const char *name);
-    void (*link_set_sriov_params_async)(NMPlatform *            self,
-                                        int                     ifindex,
-                                        guint                   num_vfs,
-                                        NMOptionBool            autoprobe,
-                                        NMPlatformAsyncCallback callback,
-                                        gpointer                callback_data,
-                                        GCancellable *          cancellable);
-    gboolean (*link_set_sriov_vfs)(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
-    gboolean (*link_set_bridge_vlans)(NMPlatform *                       self,
-                                      int                                ifindex,
-                                      gboolean                           on_master,
-                                      const NMPlatformBridgeVlan *const *vlans);
-
-    char *(*link_get_physical_port_id)(NMPlatform *self, int ifindex);
-    guint (*link_get_dev_id)(NMPlatform *self, int ifindex);
-    gboolean (*link_get_wake_on_lan)(NMPlatform *self, int ifindex);
-    gboolean (*link_get_driver_info)(NMPlatform *self,
-                                     int         ifindex,
-                                     char **     out_driver_name,
-                                     char **     out_driver_version,
-                                     char **     out_fw_version);
-
-    gboolean (*link_supports_carrier_detect)(NMPlatform *self, int ifindex);
-    gboolean (*link_supports_vlans)(NMPlatform *self, int ifindex);
-    gboolean (*link_supports_sriov)(NMPlatform *self, int ifindex);
-
-    gboolean (*link_enslave)(NMPlatform *self, int master, int slave);
-    gboolean (*link_release)(NMPlatform *self, int master, int slave);
-
-    gboolean (*link_can_assume)(NMPlatform *self, int ifindex);
-
-    int (*link_wireguard_change)(NMPlatform *                              self,
-                                 int                                       ifindex,
-                                 const NMPlatformLnkWireGuard *            lnk_wireguard,
-                                 const struct _NMPWireGuardPeer *          peers,
-                                 const NMPlatformWireGuardChangePeerFlags *peer_flags,
-                                 guint                                     peers_len,
-                                 NMPlatformWireGuardChangeFlags            change_flags);
-
-    gboolean (*link_vlan_change)(NMPlatform *            self,
-                                 int                     ifindex,
-                                 NMVlanFlags             flags_mask,
-                                 NMVlanFlags             flags_set,
-                                 gboolean                ingress_reset_all,
-                                 const NMVlanQosMapping *ingress_map,
-                                 gsize                   n_ingress_map,
-                                 gboolean                egress_reset_all,
-                                 const NMVlanQosMapping *egress_map,
-                                 gsize                   n_egress_map);
-    gboolean (*link_tun_add)(NMPlatform *            self,
-                             const char *            name,
-                             const NMPlatformLnkTun *props,
-                             const NMPlatformLink ** out_link,
-                             int *                   out_fd);
-
-    gboolean (*infiniband_partition_add)(NMPlatform *           self,
-                                         int                    parent,
-                                         int                    p_key,
-                                         const NMPlatformLink **out_link);
-    gboolean (*infiniband_partition_delete)(NMPlatform *self, int parent, int p_key);
-
-    gboolean (*wifi_get_capabilities)(NMPlatform *              self,
-                                      int                       ifindex,
-                                      NMDeviceWifiCapabilities *caps);
-    gboolean (*wifi_get_station)(NMPlatform * self,
-                                 int          ifindex,
-                                 NMEtherAddr *out_bssid,
-                                 int *        out_quality,
-                                 guint32 *    out_rate);
-    gboolean (*wifi_get_bssid)(NMPlatform *self, int ifindex, guint8 *bssid);
-    guint32 (*wifi_get_frequency)(NMPlatform *self, int ifindex);
-    int (*wifi_get_quality)(NMPlatform *self, int ifindex);
-    guint32 (*wifi_get_rate)(NMPlatform *self, int ifindex);
-    NM80211Mode (*wifi_get_mode)(NMPlatform *self, int ifindex);
-    void (*wifi_set_mode)(NMPlatform *self, int ifindex, NM80211Mode mode);
-    void (*wifi_set_powersave)(NMPlatform *self, int ifindex, guint32 powersave);
-    guint32 (*wifi_find_frequency)(NMPlatform *self, int ifindex, const guint32 *freqs);
-    void (*wifi_indicate_addressing_running)(NMPlatform *self, int ifindex, gboolean running);
-    NMSettingWirelessWakeOnWLan (*wifi_get_wake_on_wlan)(NMPlatform *self, int ifindex);
-    gboolean (*wifi_set_wake_on_wlan)(NMPlatform *                self,
-                                      int                         ifindex,
-                                      NMSettingWirelessWakeOnWLan wowl);
-
-    guint32 (*mesh_get_channel)(NMPlatform *self, int ifindex);
-    gboolean (*mesh_set_channel)(NMPlatform *self, int ifindex, guint32 channel);
-    gboolean (*mesh_set_ssid)(NMPlatform *self, int ifindex, const guint8 *ssid, gsize len);
-
-    guint16 (*wpan_get_pan_id)(NMPlatform *self, int ifindex);
-    gboolean (*wpan_set_pan_id)(NMPlatform *self, int ifindex, guint16 pan_id);
-    guint16 (*wpan_get_short_addr)(NMPlatform *self, int ifindex);
-    gboolean (*wpan_set_short_addr)(NMPlatform *self, int ifindex, guint16 short_addr);
-    gboolean (*wpan_set_channel)(NMPlatform *self, int ifindex, guint8 page, guint8 channel);
-
-    gboolean (*object_delete)(NMPlatform *self, const NMPObject *obj);
-
-    gboolean (*ip4_address_add)(NMPlatform *self,
-                                int         ifindex,
-                                in_addr_t   address,
-                                guint8      plen,
-                                in_addr_t   peer_address,
-                                in_addr_t   broadcast_address,
-                                guint32     lifetime,
-                                guint32     preferred_lft,
-                                guint32     flags,
-                                const char *label);
-    gboolean (*ip6_address_add)(NMPlatform *    self,
-                                int             ifindex,
-                                struct in6_addr address,
-                                guint8          plen,
-                                struct in6_addr peer_address,
-                                guint32         lifetime,
-                                guint32         preferred_lft,
-                                guint32         flags);
-    gboolean (*ip4_address_delete)(NMPlatform *self,
-                                   int         ifindex,
-                                   in_addr_t   address,
-                                   guint8      plen,
-                                   in_addr_t   peer_address);
-    gboolean (*ip6_address_delete)(NMPlatform *    self,
-                                   int             ifindex,
-                                   struct in6_addr address,
-                                   guint8          plen);
-
-    int (*ip_route_add)(NMPlatform *             self,
-                        NMPNlmFlags              flags,
-                        int                      addr_family,
-                        const NMPlatformIPRoute *route);
-    int (*ip_route_get)(NMPlatform *  self,
-                        int           addr_family,
-                        gconstpointer address,
-                        int           oif_ifindex,
-                        NMPObject **  out_route);
-
-    int (*routing_rule_add)(NMPlatform *                 self,
-                            NMPNlmFlags                  flags,
-                            const NMPlatformRoutingRule *routing_rule);
-
-    int (*qdisc_add)(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc);
-
-    int (*tfilter_add)(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter);
-} NMPlatformClass;
-
-/* NMPlatform signals
- *
- * Each signal handler is called with a type-specific object that provides
- * key attributes that constitute identity of the object. They may also
- * provide additional attributes for convenience.
- *
- * The object only intended to be used by the signal handler to determine
- * the current values. It is no longer valid after the signal handler exits
- * but you are free to copy the provided information and use it for later
- * reference.
- */
-#define NM_PLATFORM_SIGNAL_LINK_CHANGED         "link-changed"
-#define NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED  "ip4-address-changed"
-#define NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED  "ip6-address-changed"
-#define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED    "ip4-route-changed"
-#define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED    "ip6-route-changed"
-#define NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED "routing-rule-changed"
-#define NM_PLATFORM_SIGNAL_QDISC_CHANGED        "qdisc-changed"
-#define NM_PLATFORM_SIGNAL_TFILTER_CHANGED      "tfilter-changed"
-
-const char *nm_platform_signal_change_type_to_string(NMPlatformSignalChangeType change_type);
-
-/*****************************************************************************/
-
-GType nm_platform_get_type(void);
-
-void        nm_platform_setup(NMPlatform *instance);
-NMPlatform *nm_platform_get(void);
-
-#define NM_PLATFORM_GET (nm_platform_get())
-
-/*****************************************************************************/
-
-static inline in_addr_t
-nm_platform_ip4_broadcast_address_create(in_addr_t address, guint8 plen)
-{
-    return address | ~_nm_utils_ip4_prefix_to_netmask(plen);
-}
-
-static inline in_addr_t
-nm_platform_ip4_broadcast_address_from_addr(const NMPlatformIP4Address *addr)
-{
-    nm_assert(addr);
-
-    if (addr->use_ip4_broadcast_address)
-        return addr->broadcast_address;
-
-    /* the set broadcast-address gets ignored, and we determine a default brd base
-     * on the peer IFA_ADDRESS. */
-    if (addr->peer_address != 0u && addr->plen < 31 /* RFC3021 */)
-        return nm_platform_ip4_broadcast_address_create(addr->peer_address, addr->plen);
-    return 0u;
-}
-
-/*****************************************************************************/
-
-/**
- * nm_platform_route_table_coerce:
- * @table: the route table, in its original value as received
- *   from rtm_table/RTA_TABLE.
- *
- * Returns: returns the coerced table id, that can be stored in
- *   NMPlatformIPRoute.table_coerced.
- */
-static inline guint32
-nm_platform_route_table_coerce(guint32 table)
-{
-    /* For kernel, the default table is RT_TABLE_MAIN (254).
-     * We want that in NMPlatformIPRoute.table_coerced a numeric
-     * zero is the default. Hence, @table_coerced swaps the
-     * value 0 and 254. Use nm_platform_route_table_coerce()
-     * and nm_platform_route_table_uncoerce() to convert between
-     * the two domains. */
-    switch (table) {
-    case 0 /* RT_TABLE_UNSPEC */:
-        return 254;
-    case 254 /* RT_TABLE_MAIN */:
-        return 0;
-    default:
-        return table;
-    }
-}
-
-/**
- * nm_platform_route_table_uncoerce:
- * @table_coerced: the route table, in its coerced value
- * @normalize: whether to normalize RT_TABLE_UNSPEC to
- *   RT_TABLE_MAIN. For kernel, routes with a table id
- *   RT_TABLE_UNSPEC do not exist and are treated like
- *   RT_TABLE_MAIN.
- *
- * Returns: reverts the coerced table ID in NMPlatformIPRoute.table_coerced
- *   to the original value as kernel understands it.
- */
-static inline guint32
-nm_platform_route_table_uncoerce(guint32 table_coerced, gboolean normalize)
-{
-    /* this undoes nm_platform_route_table_coerce().  */
-    switch (table_coerced) {
-    case 0 /* RT_TABLE_UNSPEC */:
-        return 254;
-    case 254 /* RT_TABLE_MAIN */:
-        return normalize ? 254 : 0;
-    default:
-        return table_coerced;
-    }
-}
-
-static inline gboolean
-nm_platform_route_table_is_main(guint32 table)
-{
-    /* same as
-     *   nm_platform_route_table_uncoerce (table, TRUE) == RT_TABLE_MAIN
-     * and
-     *   nm_platform_route_table_uncoerce (nm_platform_route_table_coerce (table), TRUE) == RT_TABLE_MAIN
-     *
-     * That is, the function operates the same on @table and its coerced
-     * form.
-     */
-    return table == 0 || table == 254;
-}
-
-/**
- * nm_platform_route_scope_inv:
- * @scope: the route scope, either its original value, or its inverse.
- *
- * This function is useful, because the constants such as RT_SCOPE_NOWHERE
- * are 'int', so ~scope also gives an 'int'. This function gets the type
- * casts to guint8 right.
- *
- * Returns: the bitwise inverse of the route scope.
- * */
-#define nm_platform_route_scope_inv _nm_platform_uint8_inv
-static inline guint8
-_nm_platform_uint8_inv(guint8 scope)
-{
-    return (guint8) ~scope;
-}
-
-/**
- * nm_platform_route_type_coerce:
- * @table: the route type, in its original value.
- *
- * Returns: returns the coerced type, that can be stored in
- *   NMPlatformIPRoute.type_coerced.
- */
-static inline guint8
-nm_platform_route_type_coerce(guint8 type)
-{
-    switch (type) {
-    case 0 /* RTN_UNSPEC */:
-        return 1;
-    case 1 /* RTN_UNICAST */:
-        return 0;
-    default:
-        return type;
-    }
-}
-
-/**
- * nm_platform_route_type_uncoerce:
- * @table: the type table, in its coerced value
- *
- * Returns: reverts the coerced type in NMPlatformIPRoute.type_coerced
- *   to the original value as kernel understands it.
- */
-static inline guint8
-nm_platform_route_type_uncoerce(guint8 type_coerced)
-{
-    return nm_platform_route_type_coerce(type_coerced);
-}
-
-gboolean nm_platform_get_use_udev(NMPlatform *self);
-gboolean nm_platform_get_log_with_ptr(NMPlatform *self);
-
-NMPNetns *nm_platform_netns_get(NMPlatform *self);
-gboolean  nm_platform_netns_push(NMPlatform *self, NMPNetns **netns);
-
-const char *nm_link_type_to_string(NMLinkType link_type);
-
-#define NMP_SYSCTL_PATHID_ABSOLUTE(path) ((const char *) NULL), -1, (path)
-
-#define NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname, path)                        \
-    nm_sprintf_buf_unsafe_a(NM_STRLEN("net:/sys/class/net//\0") + NMP_IFNAMSIZ + ({ \
-                                const gsize _l = strlen(path);                      \
-                                                                                    \
-                                nm_assert(_l < 200);                                \
-                                _l;                                                 \
-                            }),                                                     \
-                            "net:/sys/class/net/%s/%s",                             \
-                            (ifname),                                               \
-                            (path)),                                                \
-        (dirfd), (path)
-
-#define NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, path)                            \
-    nm_sprintf_bufa(NM_STRLEN("net:/sys/class/net//" path "/\0") + NMP_IFNAMSIZ, \
-                    "net:/sys/class/net/%s/%s",                                  \
-                    (ifname),                                                    \
-                    path),                                                       \
-        (dirfd), ("" path "")
-
-int      nm_platform_sysctl_open_netdir(NMPlatform *self, int ifindex, char *out_ifname);
-gboolean nm_platform_sysctl_set(NMPlatform *self,
-                                const char *pathid,
-                                int         dirfd,
-                                const char *path,
-                                const char *value);
-void     nm_platform_sysctl_set_async(NMPlatform *            self,
-                                      const char *            pathid,
-                                      int                     dirfd,
-                                      const char *            path,
-                                      const char *const *     values,
-                                      NMPlatformAsyncCallback callback,
-                                      gpointer                data,
-                                      GCancellable *          cancellable);
-char *   nm_platform_sysctl_get(NMPlatform *self, const char *pathid, int dirfd, const char *path);
-gint32   nm_platform_sysctl_get_int32(NMPlatform *self,
-                                      const char *pathid,
-                                      int         dirfd,
-                                      const char *path,
-                                      gint32      fallback);
-gint64   nm_platform_sysctl_get_int_checked(NMPlatform *self,
-                                            const char *pathid,
-                                            int         dirfd,
-                                            const char *path,
-                                            guint       base,
-                                            gint64      min,
-                                            gint64      max,
-                                            gint64      fallback);
-
-char *nm_platform_sysctl_ip_conf_get(NMPlatform *self,
-                                     int         addr_family,
-                                     const char *ifname,
-                                     const char *property);
-
-gint64 nm_platform_sysctl_ip_conf_get_int_checked(NMPlatform *self,
-                                                  int         addr_family,
-                                                  const char *ifname,
-                                                  const char *property,
-                                                  guint       base,
-                                                  gint64      min,
-                                                  gint64      max,
-                                                  gint64      fallback);
-
-gboolean nm_platform_sysctl_ip_conf_set(NMPlatform *self,
-                                        int         addr_family,
-                                        const char *ifname,
-                                        const char *property,
-                                        const char *value);
-
-gboolean nm_platform_sysctl_ip_conf_set_int64(NMPlatform *self,
-                                              int         addr_family,
-                                              const char *ifname,
-                                              const char *property,
-                                              gint64      value);
-
-gboolean
-nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(NMPlatform *self, const char *iface, int value);
-gboolean nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(NMPlatform *self,
-                                                             const char *iface,
-                                                             guint       value_ms);
-gboolean nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(NMPlatform *self,
-                                                           const char *iface,
-                                                           guint       value_ms);
-int      nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(NMPlatform *platform,
-                                                       const char *iface,
-                                                       gboolean    consider_all,
-                                                       gboolean *  out_due_to_all);
-
-const char *nm_platform_if_indextoname(NMPlatform *self,
-                                       int         ifindex,
-                                       char        out_ifname[static 16 /* IFNAMSIZ */]);
-int         nm_platform_if_nametoindex(NMPlatform *self, const char *ifname);
-
-const NMPObject *nm_platform_link_get_obj(NMPlatform *self, int ifindex, gboolean visible_only);
-const NMPlatformLink *nm_platform_link_get(NMPlatform *self, int ifindex);
-const NMPlatformLink *nm_platform_link_get_by_ifname(NMPlatform *self, const char *ifname);
-const NMPlatformLink *nm_platform_link_get_by_address(NMPlatform *  self,
-                                                      NMLinkType    link_type,
-                                                      gconstpointer address,
-                                                      size_t        length);
-
-GPtrArray *nm_platform_link_get_all(NMPlatform *self, gboolean sort_by_name);
-
-int nm_platform_link_add(NMPlatform *           self,
-                         NMLinkType             type,
-                         const char *           name,
-                         int                    parent,
-                         const void *           address,
-                         size_t                 address_len,
-                         guint32                mtu,
-                         gconstpointer          extra_data,
-                         const NMPlatformLink **out_link);
-
-static inline int
-nm_platform_link_veth_add(NMPlatform *           self,
-                          const char *           name,
-                          const char *           peer,
-                          const NMPlatformLink **out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_VETH, name, 0, NULL, 0, 0, peer, out_link);
-}
-
-static inline int
-nm_platform_link_dummy_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_DUMMY, name, 0, NULL, 0, 0, NULL, out_link);
-}
-
-static inline int
-nm_platform_link_bridge_add(NMPlatform *               self,
-                            const char *               name,
-                            const void *               address,
-                            size_t                     address_len,
-                            guint32                    mtu,
-                            const NMPlatformLnkBridge *props,
-                            const NMPlatformLink **    out_link)
-{
-    return nm_platform_link_add(self,
-                                NM_LINK_TYPE_BRIDGE,
-                                name,
-                                0,
-                                address,
-                                address_len,
-                                mtu,
-                                props,
-                                out_link);
-}
-
-static inline int
-nm_platform_link_bond_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, NULL, out_link);
-}
-
-static inline int
-nm_platform_link_team_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_TEAM, name, 0, NULL, 0, 0, NULL, out_link);
-}
-
-static inline int
-nm_platform_link_wireguard_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_WIREGUARD, name, 0, NULL, 0, 0, NULL, out_link);
-}
-
-static inline int
-nm_platform_link_gre_add(NMPlatform *            self,
-                         const char *            name,
-                         const void *            address,
-                         size_t                  address_len,
-                         const NMPlatformLnkGre *props,
-                         const NMPlatformLink ** out_link)
-{
-    g_return_val_if_fail(props, -NME_BUG);
-
-    return nm_platform_link_add(self,
-                                props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE,
-                                name,
-                                0,
-                                address,
-                                address_len,
-                                0,
-                                props,
-                                out_link);
-}
-
-static inline int
-nm_platform_link_sit_add(NMPlatform *            self,
-                         const char *            name,
-                         const NMPlatformLnkSit *props,
-                         const NMPlatformLink ** out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_SIT, name, 0, NULL, 0, 0, props, out_link);
-}
-
-static inline int
-nm_platform_link_vlan_add(NMPlatform *           self,
-                          const char *           name,
-                          int                    parent,
-                          int                    vlanid,
-                          guint32                vlanflags,
-                          const NMPlatformLink **out_link)
-{
-    g_return_val_if_fail(parent >= 0, -NME_BUG);
-    g_return_val_if_fail(vlanid >= 0, -NME_BUG);
-
-    return nm_platform_link_add(self,
-                                NM_LINK_TYPE_VLAN,
-                                name,
-                                parent,
-                                NULL,
-                                0,
-                                0,
-                                &((NMPlatformLnkVlan){
-                                    .id    = vlanid,
-                                    .flags = vlanflags,
-                                }),
-                                out_link);
-}
-
-static inline int
-nm_platform_link_vrf_add(NMPlatform *            self,
-                         const char *            name,
-                         const NMPlatformLnkVrf *props,
-                         const NMPlatformLink ** out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_VRF, name, 0, NULL, 0, 0, props, out_link);
-}
-
-static inline int
-nm_platform_link_vxlan_add(NMPlatform *              self,
-                           const char *              name,
-                           const NMPlatformLnkVxlan *props,
-                           const NMPlatformLink **   out_link)
-{
-    return nm_platform_link_add(self, NM_LINK_TYPE_VXLAN, name, 0, NULL, 0, 0, props, out_link);
-}
-
-static inline int
-nm_platform_link_6lowpan_add(NMPlatform *           self,
-                             const char *           name,
-                             int                    parent,
-                             const NMPlatformLink **out_link)
-{
-    return nm_platform_link_add(self,
-                                NM_LINK_TYPE_6LOWPAN,
-                                name,
-                                parent,
-                                NULL,
-                                0,
-                                0,
-                                NULL,
-                                out_link);
-}
-
-static inline int
-nm_platform_link_ip6tnl_add(NMPlatform *               self,
-                            const char *               name,
-                            const NMPlatformLnkIp6Tnl *props,
-                            const NMPlatformLink **    out_link)
-{
-    g_return_val_if_fail(props, -NME_BUG);
-    g_return_val_if_fail(!props->is_gre, -NME_BUG);
-
-    return nm_platform_link_add(self, NM_LINK_TYPE_IP6TNL, name, 0, NULL, 0, 0, props, out_link);
-}
-
-static inline int
-nm_platform_link_ip6gre_add(NMPlatform *               self,
-                            const char *               name,
-                            const void *               address,
-                            size_t                     address_len,
-                            const NMPlatformLnkIp6Tnl *props,
-                            const NMPlatformLink **    out_link)
-{
-    g_return_val_if_fail(props, -NME_BUG);
-    g_return_val_if_fail(props->is_gre, -NME_BUG);
-
-    return nm_platform_link_add(self,
-                                props->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE,
-                                name,
-                                0,
-                                address,
-                                address_len,
-                                0,
-                                props,
-                                out_link);
-}
-
-static inline int
-nm_platform_link_ipip_add(NMPlatform *             self,
-                          const char *             name,
-                          const NMPlatformLnkIpIp *props,
-                          const NMPlatformLink **  out_link)
-{
-    g_return_val_if_fail(props, -NME_BUG);
-
-    return nm_platform_link_add(self, NM_LINK_TYPE_IPIP, name, 0, NULL, 0, 0, props, out_link);
-}
-
-static inline int
-nm_platform_link_macsec_add(NMPlatform *               self,
-                            const char *               name,
-                            int                        parent,
-                            const NMPlatformLnkMacsec *props,
-                            const NMPlatformLink **    out_link)
-{
-    g_return_val_if_fail(props, -NME_BUG);
-    g_return_val_if_fail(parent > 0, -NME_BUG);
-
-    return nm_platform_link_add(self,
-                                NM_LINK_TYPE_MACSEC,
-                                name,
-                                parent,
-                                NULL,
-                                0,
-                                0,
-                                props,
-                                out_link);
-}
-
-static inline int
-nm_platform_link_macvlan_add(NMPlatform *                self,
-                             const char *                name,
-                             int                         parent,
-                             const NMPlatformLnkMacvlan *props,
-                             const NMPlatformLink **     out_link)
-{
-    g_return_val_if_fail(props, -NME_BUG);
-    g_return_val_if_fail(parent > 0, -NME_BUG);
-
-    return nm_platform_link_add(self,
-                                props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN,
-                                name,
-                                parent,
-                                NULL,
-                                0,
-                                0,
-                                props,
-                                out_link);
-}
-
-gboolean nm_platform_link_delete(NMPlatform *self, int ifindex);
-
-gboolean nm_platform_link_set_netns(NMPlatform *self, int ifindex, int netns_fd);
-
-struct _NMDedupMultiHeadEntry;
-struct _NMPLookup;
-const struct _NMDedupMultiHeadEntry *nm_platform_lookup(NMPlatform *             self,
-                                                        const struct _NMPLookup *lookup);
-
-#define nm_platform_iter_obj_for_each(iter, self, lookup, obj)                   \
-    for (nm_dedup_multi_iter_init((iter), nm_platform_lookup((self), (lookup))); \
-         nm_platform_dedup_multi_iter_next_obj((iter), (obj), NMP_OBJECT_TYPE_UNKNOWN);)
-
-gboolean nm_platform_lookup_predicate_routes_main(const NMPObject *obj, gpointer user_data);
-gboolean nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel(const NMPObject *obj,
-                                                                     gpointer         user_data);
-
-GPtrArray *nm_platform_lookup_clone(NMPlatform *             self,
-                                    const struct _NMPLookup *lookup,
-                                    NMPObjectPredicateFunc   predicate,
-                                    gpointer                 user_data);
-
-/* convenience methods to lookup the link and access fields of NMPlatformLink. */
-int         nm_platform_link_get_ifindex(NMPlatform *self, const char *name);
-const char *nm_platform_link_get_name(NMPlatform *self, int ifindex);
-NMLinkType  nm_platform_link_get_type(NMPlatform *self, int ifindex);
-gboolean    nm_platform_link_is_software(NMPlatform *self, int ifindex);
-int         nm_platform_link_get_ifi_flags(NMPlatform *self, int ifindex, guint requested_flags);
-gboolean    nm_platform_link_is_up(NMPlatform *self, int ifindex);
-gboolean    nm_platform_link_is_connected(NMPlatform *self, int ifindex);
-gboolean    nm_platform_link_uses_arp(NMPlatform *self, int ifindex);
-guint32     nm_platform_link_get_mtu(NMPlatform *self, int ifindex);
-gboolean    nm_platform_link_get_user_ipv6ll_enabled(NMPlatform *self, int ifindex);
-
-gconstpointer nm_platform_link_get_address(NMPlatform *self, int ifindex, size_t *length);
-
-int nm_platform_link_get_master(NMPlatform *self, int slave);
-
-gboolean nm_platform_link_can_assume(NMPlatform *self, int ifindex);
-
-gboolean    nm_platform_link_get_unmanaged(NMPlatform *self, int ifindex, gboolean *unmanaged);
-gboolean    nm_platform_link_supports_slaves(NMPlatform *self, int ifindex);
-const char *nm_platform_link_get_type_name(NMPlatform *self, int ifindex);
-
-gboolean nm_platform_link_refresh(NMPlatform *self, int ifindex);
-void     nm_platform_process_events(NMPlatform *self);
-
-const NMPlatformLink *
-nm_platform_process_events_ensure_link(NMPlatform *self, int ifindex, const char *ifname);
-
-gboolean nm_platform_link_set_up(NMPlatform *self, int ifindex, gboolean *out_no_firmware);
-gboolean nm_platform_link_set_down(NMPlatform *self, int ifindex);
-gboolean nm_platform_link_set_arp(NMPlatform *self, int ifindex);
-gboolean nm_platform_link_set_noarp(NMPlatform *self, int ifindex);
-
-const char *nm_platform_link_get_udi(NMPlatform *self, int ifindex);
-const char *nm_platform_link_get_path(NMPlatform *self, int ifindex);
-
-struct udev_device *nm_platform_link_get_udev_device(NMPlatform *self, int ifindex);
-
-int      nm_platform_link_set_user_ipv6ll_enabled(NMPlatform *self, int ifindex, gboolean enabled);
-gboolean nm_platform_link_set_ipv6_token(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid);
-
-gboolean
-nm_platform_link_get_permanent_address(NMPlatform *self, int ifindex, guint8 *buf, size_t *length);
-int nm_platform_link_set_address(NMPlatform *self, int ifindex, const void *address, size_t length);
-int nm_platform_link_set_mtu(NMPlatform *self, int ifindex, guint32 mtu);
-gboolean nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name);
-
-void nm_platform_link_set_sriov_params_async(NMPlatform *            self,
-                                             int                     ifindex,
-                                             guint                   num_vfs,
-                                             NMOptionBool            autoprobe,
-                                             NMPlatformAsyncCallback callback,
-                                             gpointer                callback_data,
-                                             GCancellable *          cancellable);
-
-gboolean
-nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
-gboolean nm_platform_link_set_bridge_vlans(NMPlatform *                       self,
-                                           int                                ifindex,
-                                           gboolean                           on_master,
-                                           const NMPlatformBridgeVlan *const *vlans);
-
-char *   nm_platform_link_get_physical_port_id(NMPlatform *self, int ifindex);
-guint    nm_platform_link_get_dev_id(NMPlatform *self, int ifindex);
-gboolean nm_platform_link_get_wake_on_lan(NMPlatform *self, int ifindex);
-gboolean nm_platform_link_get_driver_info(NMPlatform *self,
-                                          int         ifindex,
-                                          char **     out_driver_name,
-                                          char **     out_driver_version,
-                                          char **     out_fw_version);
-
-gboolean nm_platform_link_supports_carrier_detect(NMPlatform *self, int ifindex);
-gboolean nm_platform_link_supports_vlans(NMPlatform *self, int ifindex);
-gboolean nm_platform_link_supports_sriov(NMPlatform *self, int ifindex);
-
-gboolean nm_platform_link_enslave(NMPlatform *self, int master, int slave);
-gboolean nm_platform_link_release(NMPlatform *self, int master, int slave);
-
-gboolean nm_platform_sysctl_master_set_option(NMPlatform *self,
-                                              int         ifindex,
-                                              const char *option,
-                                              const char *value);
-char *   nm_platform_sysctl_master_get_option(NMPlatform *self, int ifindex, const char *option);
-gboolean nm_platform_sysctl_slave_set_option(NMPlatform *self,
-                                             int         ifindex,
-                                             const char *option,
-                                             const char *value);
-char *   nm_platform_sysctl_slave_get_option(NMPlatform *self, int ifindex, const char *option);
-
-const NMPObject *nm_platform_link_get_lnk(NMPlatform *           self,
-                                          int                    ifindex,
-                                          NMLinkType             link_type,
-                                          const NMPlatformLink **out_link);
-const NMPlatformLnkBridge *
-nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkGre *
-nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkGre *
-nm_platform_link_get_lnk_gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkIp6Tnl *
-nm_platform_link_get_lnk_ip6tnl(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkIp6Tnl *
-nm_platform_link_get_lnk_ip6gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkIp6Tnl *
-nm_platform_link_get_lnk_ip6gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkIpIp *
-nm_platform_link_get_lnk_ipip(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkInfiniband *
-nm_platform_link_get_lnk_infiniband(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkIpIp *
-nm_platform_link_get_lnk_ipip(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkMacsec *
-nm_platform_link_get_lnk_macsec(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkMacvlan *
-nm_platform_link_get_lnk_macvlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkMacvlan *
-nm_platform_link_get_lnk_macvtap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkSit *
-nm_platform_link_get_lnk_sit(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkTun *
-nm_platform_link_get_lnk_tun(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkVlan *
-nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkVrf *
-nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkVxlan *
-nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkWireGuard *
-nm_platform_link_get_lnk_wireguard(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-
-gboolean nm_platform_link_vlan_set_ingress_map(NMPlatform *self, int ifindex, int from, int to);
-gboolean nm_platform_link_vlan_set_egress_map(NMPlatform *self, int ifindex, int from, int to);
-gboolean nm_platform_link_vlan_change(NMPlatform *            self,
-                                      int                     ifindex,
-                                      NMVlanFlags             flags_mask,
-                                      NMVlanFlags             flags_set,
-                                      gboolean                ingress_reset_all,
-                                      const NMVlanQosMapping *ingress_map,
-                                      gsize                   n_ingress_map,
-                                      gboolean                egress_reset_all,
-                                      const NMVlanQosMapping *egress_map,
-                                      gsize                   n_egress_map);
-
-int      nm_platform_link_infiniband_add(NMPlatform *           self,
-                                         int                    parent,
-                                         int                    p_key,
-                                         const NMPlatformLink **out_link);
-int      nm_platform_link_infiniband_delete(NMPlatform *self, int parent, int p_key);
-gboolean nm_platform_link_infiniband_get_properties(NMPlatform * self,
-                                                    int          ifindex,
-                                                    int *        parent,
-                                                    int *        p_key,
-                                                    const char **mode);
-
-gboolean nm_platform_link_veth_get_properties(NMPlatform *self, int ifindex, int *out_peer_ifindex);
-gboolean nm_platform_link_tun_get_properties(NMPlatform *      self,
-                                             int               ifindex,
-                                             NMPlatformLnkTun *out_properties);
-
-gboolean
-nm_platform_wifi_get_capabilities(NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps);
-guint32     nm_platform_wifi_get_frequency(NMPlatform *self, int ifindex);
-gboolean    nm_platform_wifi_get_station(NMPlatform * self,
-                                         int          ifindex,
-                                         NMEtherAddr *out_bssid,
-                                         int *        out_quality,
-                                         guint32 *    out_rate);
-NM80211Mode nm_platform_wifi_get_mode(NMPlatform *self, int ifindex);
-void        nm_platform_wifi_set_mode(NMPlatform *self, int ifindex, NM80211Mode mode);
-void        nm_platform_wifi_set_powersave(NMPlatform *self, int ifindex, guint32 powersave);
-guint32     nm_platform_wifi_find_frequency(NMPlatform *self, int ifindex, const guint32 *freqs);
-void nm_platform_wifi_indicate_addressing_running(NMPlatform *self, int ifindex, gboolean running);
-NMSettingWirelessWakeOnWLan nm_platform_wifi_get_wake_on_wlan(NMPlatform *self, int ifindex);
-gboolean
-nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl);
-
-guint32  nm_platform_mesh_get_channel(NMPlatform *self, int ifindex);
-gboolean nm_platform_mesh_set_channel(NMPlatform *self, int ifindex, guint32 channel);
-gboolean nm_platform_mesh_set_ssid(NMPlatform *self, int ifindex, const guint8 *ssid, gsize len);
-
-guint16  nm_platform_wpan_get_pan_id(NMPlatform *self, int ifindex);
-gboolean nm_platform_wpan_set_pan_id(NMPlatform *self, int ifindex, guint16 pan_id);
-guint16  nm_platform_wpan_get_short_addr(NMPlatform *self, int ifindex);
-gboolean nm_platform_wpan_set_short_addr(NMPlatform *self, int ifindex, guint16 short_addr);
-gboolean nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 channel);
-
-void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen);
-const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr);
-
-const NMPlatformIP4Address *nm_platform_ip4_address_get(NMPlatform *self,
-                                                        int         ifindex,
-                                                        in_addr_t   address,
-                                                        guint8      plen,
-                                                        in_addr_t   peer_address);
-
-int      nm_platform_link_sit_add(NMPlatform *            self,
-                                  const char *            name,
-                                  const NMPlatformLnkSit *props,
-                                  const NMPlatformLink ** out_link);
-int      nm_platform_link_tun_add(NMPlatform *            self,
-                                  const char *            name,
-                                  const NMPlatformLnkTun *props,
-                                  const NMPlatformLink ** out_link,
-                                  int *                   out_fd);
-gboolean nm_platform_link_6lowpan_get_properties(NMPlatform *self, int ifindex, int *out_parent);
-
-int
-nm_platform_link_wireguard_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link);
-
-int nm_platform_link_wireguard_change(NMPlatform *                              self,
-                                      int                                       ifindex,
-                                      const NMPlatformLnkWireGuard *            lnk_wireguard,
-                                      const struct _NMPWireGuardPeer *          peers,
-                                      const NMPlatformWireGuardChangePeerFlags *peer_flags,
-                                      guint                                     peers_len,
-                                      NMPlatformWireGuardChangeFlags            change_flags);
-
-const NMPlatformIP6Address *
-nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address);
-
-gboolean nm_platform_object_delete(NMPlatform *self, const NMPObject *route);
-
-gboolean nm_platform_ip4_address_add(NMPlatform *self,
-                                     int         ifindex,
-                                     in_addr_t   address,
-                                     guint8      plen,
-                                     in_addr_t   peer_address,
-                                     in_addr_t   broadcast_address,
-                                     guint32     lifetime,
-                                     guint32     preferred_lft,
-                                     guint32     flags,
-                                     const char *label);
-gboolean nm_platform_ip6_address_add(NMPlatform *    self,
-                                     int             ifindex,
-                                     struct in6_addr address,
-                                     guint8          plen,
-                                     struct in6_addr peer_address,
-                                     guint32         lifetime,
-                                     guint32         preferred_lft,
-                                     guint32         flags);
-gboolean nm_platform_ip4_address_delete(NMPlatform *self,
-                                        int         ifindex,
-                                        in_addr_t   address,
-                                        guint8      plen,
-                                        in_addr_t   peer_address);
-gboolean
-nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen);
-
-gboolean nm_platform_ip_address_sync(NMPlatform *self,
-                                     int         addr_family,
-                                     int         ifindex,
-                                     GPtrArray * known_addresses,
-                                     GPtrArray * addresses_prune);
-
-GPtrArray *nm_platform_ip_address_get_prune_list(NMPlatform *self,
-                                                 int         addr_family,
-                                                 int         ifindex,
-                                                 gboolean    exclude_ipv6_temporary_addrs);
-
-static inline gboolean
-_nm_platform_ip_address_sync(NMPlatform *self,
-                             int         addr_family,
-                             int         ifindex,
-                             GPtrArray * known_addresses,
-                             gboolean    full_sync)
-{
-    gs_unref_ptrarray GPtrArray *addresses_prune = NULL;
-
-    addresses_prune = nm_platform_ip_address_get_prune_list(self, addr_family, ifindex, !full_sync);
-    return nm_platform_ip_address_sync(self,
-                                       addr_family,
-                                       ifindex,
-                                       known_addresses,
-                                       addresses_prune);
-}
-
-static inline gboolean
-nm_platform_ip4_address_sync(NMPlatform *self, int ifindex, GPtrArray *known_addresses)
-{
-    return _nm_platform_ip_address_sync(self, AF_INET, ifindex, known_addresses, TRUE);
-}
-
-static inline gboolean
-nm_platform_ip6_address_sync(NMPlatform *self,
-                             int         ifindex,
-                             GPtrArray * known_addresses,
-                             gboolean    full_sync)
-{
-    return _nm_platform_ip_address_sync(self, AF_INET6, ifindex, known_addresses, full_sync);
-}
-
-gboolean nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex);
-
-static inline gconstpointer
-nm_platform_ip_address_get_peer_address(int addr_family, const NMPlatformIPAddress *addr)
-{
-    nm_assert_addr_family(addr_family);
-    nm_assert(addr);
-
-    if (NM_IS_IPv4(addr_family))
-        return &((NMPlatformIP4Address *) addr)->peer_address;
-    return &((NMPlatformIP6Address *) addr)->peer_address;
-}
-
-void nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route);
-
-static inline guint32
-nm_platform_ip4_route_get_effective_metric(const NMPlatformIP4Route *r)
-{
-    nm_assert(r);
-
-    return r->metric_any ? nm_add_clamped_u32(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, r->metric)
-                         : r->metric;
-}
-
-static inline guint32
-nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r)
-{
-    nm_assert(r);
-
-    return r->metric_any ? nm_add_clamped_u32(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, r->metric)
-                         : r->metric;
-}
-
-static inline guint32
-nm_platform_ip_route_get_effective_table(const NMPlatformIPRoute *r)
-{
-    nm_assert(r);
-    nm_assert(!r->table_any || r->table_coerced == 0);
-
-    return r->table_any ? 254u /* RT_TABLE_MAIN */
-                        : nm_platform_route_table_uncoerce(r->table_coerced, TRUE);
-}
-
-static inline gconstpointer
-nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route)
-{
-    nm_assert_addr_family(addr_family);
-    nm_assert(route);
-
-    if (NM_IS_IPv4(addr_family))
-        return &((NMPlatformIP4Route *) route)->gateway;
-    return &((NMPlatformIP6Route *) route)->gateway;
-}
-
-int nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route);
-int nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route);
-int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route);
-
-GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform *           self,
-                                               int                    addr_family,
-                                               int                    ifindex,
-                                               NMIPRouteTableSyncMode route_table_sync);
-
-gboolean nm_platform_ip_route_sync(NMPlatform *self,
-                                   int         addr_family,
-                                   int         ifindex,
-                                   GPtrArray * routes,
-                                   GPtrArray * routes_prune,
-                                   GPtrArray **out_temporary_not_available);
-
-gboolean nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex);
-
-int nm_platform_ip_route_get(NMPlatform *  self,
-                             int           addr_family,
-                             gconstpointer address,
-                             int           oif_ifindex,
-                             NMPObject **  out_route);
-
-int nm_platform_routing_rule_add(NMPlatform *                 self,
-                                 NMPNlmFlags                  flags,
-                                 const NMPlatformRoutingRule *routing_rule);
-
-int      nm_platform_qdisc_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc);
-gboolean nm_platform_qdisc_sync(NMPlatform *self, int ifindex, GPtrArray *known_qdiscs);
-
-int nm_platform_tfilter_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter);
-gboolean nm_platform_tfilter_sync(NMPlatform *self, int ifindex, GPtrArray *known_tfilters);
-
-const char *nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len);
-const char *nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len);
-const char *
-nm_platform_lnk_infiniband_to_string(const NMPlatformLnkInfiniband *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_macsec_to_string(const NMPlatformLnkMacsec *lnk, char *buf, gsize len);
-const char *
-nm_platform_lnk_macvlan_to_string(const NMPlatformLnkMacvlan *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len);
-const char *
-nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len);
-const char *
-nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len);
-const char *
-nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len);
-const char *nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len);
-const char *nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len);
-const char *
-nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len);
-const char *nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len);
-const char *nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len);
-const char *nm_platform_vf_to_string(const NMPlatformVF *vf, char *buf, gsize len);
-const char *
-nm_platform_bridge_vlan_to_string(const NMPlatformBridgeVlan *vlan, char *buf, gsize len);
-
-const char *nm_platform_vlan_qos_mapping_to_string(const char *            name,
-                                                   const NMVlanQosMapping *map,
-                                                   gsize                   n_map,
-                                                   char *                  buf,
-                                                   gsize                   len);
-
-const char *
-nm_platform_wireguard_peer_to_string(const struct _NMPWireGuardPeer *peer, char *buf, gsize len);
-
-int nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b);
-int nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b);
-int nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b);
-int nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a,
-                                   const NMPlatformLnkInfiniband *b);
-int nm_platform_lnk_ip6tnl_cmp(const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b);
-int nm_platform_lnk_ipip_cmp(const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b);
-int nm_platform_lnk_macsec_cmp(const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b);
-int nm_platform_lnk_macvlan_cmp(const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b);
-int nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b);
-int nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b);
-int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b);
-int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b);
-int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b);
-int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b);
-int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b);
-int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b);
-
-int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1,
-                                            const NMPlatformIP4Address *a2);
-
-int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1,
-                                            const NMPlatformIP6Address *a2,
-                                            gboolean                    prefer_temp);
-
-GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex);
-
-int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
-                              const NMPlatformIP4Route *b,
-                              NMPlatformIPRouteCmpType  cmp_type);
-int nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
-                              const NMPlatformIP6Route *b,
-                              NMPlatformIPRouteCmpType  cmp_type);
-
-static inline int
-nm_platform_ip4_route_cmp_full(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b)
-{
-    return nm_platform_ip4_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
-}
-
-static inline int
-nm_platform_ip6_route_cmp_full(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b)
-{
-    return nm_platform_ip6_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
-}
-
-int nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a,
-                                 const NMPlatformRoutingRule *b,
-                                 NMPlatformRoutingRuleCmpType cmp_type);
-
-static inline int
-nm_platform_routing_rule_cmp_full(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b)
-{
-    return nm_platform_routing_rule_cmp(a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL);
-}
-
-int nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b);
-int nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a,
-                               const NMPlatformQdisc *b,
-                               gboolean               compare_handle);
-int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b);
-
-void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h);
-void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h);
-void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h);
-void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
-                                       NMPlatformIPRouteCmpType  cmp_type,
-                                       NMHashState *             h);
-void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
-                                       NMPlatformIPRouteCmpType  cmp_type,
-                                       NMHashState *             h);
-void nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj,
-                                          NMPlatformRoutingRuleCmpType cmp_type,
-                                          NMHashState *                h);
-void nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h);
-void nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h);
-void nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h);
-void nm_platform_lnk_ip6tnl_hash_update(const NMPlatformLnkIp6Tnl *obj, NMHashState *h);
-void nm_platform_lnk_ipip_hash_update(const NMPlatformLnkIpIp *obj, NMHashState *h);
-void nm_platform_lnk_macsec_hash_update(const NMPlatformLnkMacsec *obj, NMHashState *h);
-void nm_platform_lnk_macvlan_hash_update(const NMPlatformLnkMacvlan *obj, NMHashState *h);
-void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h);
-void nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h);
-void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h);
-void nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h);
-void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h);
-void nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h);
-
-void nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h);
-void nm_platform_tfilter_hash_update(const NMPlatformTfilter *obj, NMHashState *h);
-
-#define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162)
-
-const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len);
-const char *nm_platform_link_inet6_addrgenmode2str(guint8 mode, char *buf, gsize len);
-const char *nm_platform_addr_flags2str(unsigned flags, char *buf, gsize len);
-const char *nm_platform_route_scope2str(int scope, char *buf, gsize len);
-
-int nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b);
-
-gboolean nm_platform_ethtool_set_wake_on_lan(NMPlatform *             self,
-                                             int                      ifindex,
-                                             _NMSettingWiredWakeOnLan wol,
-                                             const char *             wol_password);
-gboolean nm_platform_ethtool_set_link_settings(NMPlatform *             self,
-                                               int                      ifindex,
-                                               gboolean                 autoneg,
-                                               guint32                  speed,
-                                               NMPlatformLinkDuplexType duplex);
-gboolean nm_platform_ethtool_get_link_settings(NMPlatform *              self,
-                                               int                       ifindex,
-                                               gboolean *                out_autoneg,
-                                               guint32 *                 out_speed,
-                                               NMPlatformLinkDuplexType *out_duplex);
-
-NMEthtoolFeatureStates *nm_platform_ethtool_get_link_features(NMPlatform *self, int ifindex);
-gboolean                nm_platform_ethtool_set_features(
-                   NMPlatform *                  self,
-                   int                           ifindex,
-                   const NMEthtoolFeatureStates *features,
-                   const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */,
-                   gboolean            do_set /* or reset */);
-
-gboolean nm_platform_ethtool_get_link_coalesce(NMPlatform *            self,
-                                               int                     ifindex,
-                                               NMEthtoolCoalesceState *coalesce);
-
-gboolean nm_platform_ethtool_set_coalesce(NMPlatform *                  self,
-                                          int                           ifindex,
-                                          const NMEthtoolCoalesceState *coalesce);
-
-gboolean nm_platform_ethtool_get_link_ring(NMPlatform *self, int ifindex, NMEthtoolRingState *ring);
-
-gboolean
-nm_platform_ethtool_set_ring(NMPlatform *self, int ifindex, const NMEthtoolRingState *ring);
-
-void nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self,
-                                             int         ifindex,
-                                             GPtrArray * ip4_dev_route_blacklist);
-
-struct _NMDedupMultiIndex *nm_platform_get_multi_idx(NMPlatform *self);
-
-/*****************************************************************************/
-
-gboolean nm_platform_ip6_address_match(const NMPlatformIP6Address *addr,
-                                       NMPlatformMatchFlags        match_flag);
-
-#endif /* __NETWORKMANAGER_PLATFORM_H__ */
diff --git a/src/core/platform/nmp-object.c b/src/core/platform/nmp-object.c
deleted file mode 100644
index 6a26595e..00000000
--- a/src/core/platform/nmp-object.c
+++ /dev/null
@@ -1,3457 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2015 - 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nmp-object.h"
-
-#include <unistd.h>
-#include <linux/rtnetlink.h>
-#include <linux/if.h>
-#include <libudev.h>
-
-#include "nm-utils.h"
-#include "nm-glib-aux/nm-secret-utils.h"
-
-#include "nm-core-utils.h"
-#include "nm-platform/nm-platform-utils.h"
-
-#include "wifi/nm-wifi-utils.h"
-#include "wpan/nm-wpan-utils.h"
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN LOGD_PLATFORM
-#define _NMLOG(level, obj, ...)                                               \
-    G_STMT_START                                                              \
-    {                                                                         \
-        const NMLogLevel __level = (level);                                   \
-                                                                              \
-        if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) {                     \
-            const NMPObject *const __obj = (obj);                             \
-                                                                              \
-            _nm_log(__level,                                                  \
-                    _NMLOG_DOMAIN,                                            \
-                    0,                                                        \
-                    NULL,                                                     \
-                    NULL,                                                     \
-                    "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-                    __obj,                                                    \
-                    (__obj ? NMP_OBJECT_GET_CLASS(__obj)->obj_type_name       \
-                           : "???") _NM_UTILS_MACRO_REST(__VA_ARGS__));       \
-        }                                                                     \
-    }                                                                         \
-    G_STMT_END
-
-/*****************************************************************************/
-
-typedef struct {
-    NMDedupMultiIdxType parent;
-    NMPCacheIdType      cache_id_type;
-} DedupMultiIdxType;
-
-struct _NMPCache {
-    /* the cache contains only one hash table for all object types, and similarly
-     * it contains only one NMMultiIndex.
-     * This works, because different object types don't ever compare equal and
-     * because their index ids also don't overlap.
-     *
-     * For routes and addresses, the cache contains an address if (and only if) the
-     * object was reported via netlink.
-     * For links, the cache contain a link if it was reported by either netlink
-     * or udev. That means, a link object can be alive, even if it was already
-     * removed via netlink.
-     *
-     * This effectively merges the udev-device cache into the NMPCache.
-     */
-
-    NMDedupMultiIndex *multi_idx;
-
-    /* an idx_type entry for each NMP_CACHE_ID_TYPE. Note that NONE (zero)
-     * is skipped, so the index is shifted by one: idx_type[cache_id_type - 1].
-     *
-     * Don't bother, use _idx_type_get() instead! */
-    DedupMultiIdxType idx_types[NMP_CACHE_ID_TYPE_MAX];
-
-    gboolean use_udev;
-};
-
-/*****************************************************************************/
-
-int
-nm_sock_addr_union_cmp(const NMSockAddrUnion *a, const NMSockAddrUnion *b)
-{
-    nm_assert(!a || NM_IN_SET(a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6));
-    nm_assert(!b || NM_IN_SET(b->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6));
-
-    NM_CMP_SELF(a, b);
-
-    NM_CMP_FIELD(a, b, sa.sa_family);
-    switch (a->sa.sa_family) {
-    case AF_INET:
-        NM_CMP_DIRECT(ntohl(a->in.sin_addr.s_addr), ntohl(b->in.sin_addr.s_addr));
-        NM_CMP_DIRECT(htons(a->in.sin_port), htons(b->in.sin_port));
-        break;
-    case AF_INET6:
-        NM_CMP_DIRECT_IN6ADDR(&a->in6.sin6_addr, &b->in6.sin6_addr);
-        NM_CMP_DIRECT(htons(a->in6.sin6_port), htons(b->in6.sin6_port));
-        NM_CMP_FIELD(a, b, in6.sin6_scope_id);
-        NM_CMP_FIELD(a, b, in6.sin6_flowinfo);
-        break;
-    }
-    return 0;
-}
-
-void
-nm_sock_addr_union_hash_update(const NMSockAddrUnion *a, NMHashState *h)
-{
-    if (!a) {
-        nm_hash_update_val(h, 1241364739u);
-        return;
-    }
-
-    nm_assert(NM_IN_SET(a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6));
-
-    switch (a->sa.sa_family) {
-    case AF_INET:
-        nm_hash_update_vals(h, a->in.sin_family, a->in.sin_addr.s_addr, a->in.sin_port);
-        return;
-    case AF_INET6:
-        nm_hash_update_vals(h,
-                            a->in6.sin6_family,
-                            a->in6.sin6_addr,
-                            a->in6.sin6_port,
-                            a->in6.sin6_scope_id,
-                            a->in6.sin6_flowinfo);
-        return;
-    default:
-        nm_hash_update_val(h, a->sa.sa_family);
-        return;
-    }
-}
-
-/**
- * nm_sock_addr_union_cpy:
- * @dst: the destination #NMSockAddrUnion. It will always be fully initialized,
- *   to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of
- *   error).
- * @src: (allow-none): the source buffer with an sockaddr to copy. It may be unaligned in
- *   memory. If not %NULL, the buffer must be at least large enough to contain
- *   sa.sa_family, and then, depending on sa.sa_family, it must be large enough
- *   to hold struct sockaddr_in or struct sockaddr_in6.
- *
- * @dst will always be fully initialized (including setting all un-used bytes to zero).
- */
-void
-nm_sock_addr_union_cpy(NMSockAddrUnion *dst,
-                       gconstpointer    src /* unaligned (const NMSockAddrUnion *) */)
-{
-    struct sockaddr sa;
-    gsize           src_len;
-
-    nm_assert(dst);
-
-    *dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC;
-
-    if (!src)
-        return;
-
-    memcpy(&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof(sa.sa_family));
-
-    if (sa.sa_family == AF_INET)
-        src_len = sizeof(struct sockaddr_in);
-    else if (sa.sa_family == AF_INET6)
-        src_len = sizeof(struct sockaddr_in6);
-    else
-        return;
-
-    memcpy(dst, src, src_len);
-    nm_assert(dst->sa.sa_family == sa.sa_family);
-}
-
-/**
- * nm_sock_addr_union_cpy_untrusted:
- * @dst: the destination #NMSockAddrUnion. It will always be fully initialized,
- *   to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of
- *   error).
- * @src: the source buffer with an sockaddr to copy. It may be unaligned in
- *   memory.
- * @src_len: the length of @src in bytes.
- *
- * The function requires @src_len to be either sizeof(struct sockaddr_in) or sizeof (struct sockaddr_in6).
- * If that's the case, then @src will be interpreted as such structure (unaligned), and
- * accessed. It will check sa.sa_family to match the expected sizes, and if it does, the
- * struct will be copied.
- *
- * On any failure, @dst will be set to sa.sa_family AF_UNSPEC.
- * @dst will always be fully initialized (including setting all un-used bytes to zero).
- */
-void
-nm_sock_addr_union_cpy_untrusted(NMSockAddrUnion *dst,
-                                 gconstpointer    src /* unaligned (const NMSockAddrUnion *) */,
-                                 gsize            src_len)
-{
-    int             f_expected;
-    struct sockaddr sa;
-
-    nm_assert(dst);
-
-    *dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC;
-
-    if (src_len == sizeof(struct sockaddr_in))
-        f_expected = AF_INET;
-    else if (src_len == sizeof(struct sockaddr_in6))
-        f_expected = AF_INET6;
-    else
-        return;
-
-    memcpy(&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof(sa.sa_family));
-
-    if (sa.sa_family != f_expected)
-        return;
-
-    memcpy(dst, src, src_len);
-    nm_assert(dst->sa.sa_family == sa.sa_family);
-}
-
-const char *
-nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len)
-{
-    char s_addr[NM_UTILS_INET_ADDRSTRLEN];
-    char s_scope_id[40];
-
-    if (!nm_utils_to_string_buffer_init_null(sa, &buf, &len))
-        return buf;
-
-    /* maybe we should use getnameinfo(), but here implement it ourself.
-     *
-     * We want to see the actual bytes for debugging (as we understand them),
-     * and now what getnameinfo() makes of it. Also, it's simpler this way. */
-
-    switch (sa->sa.sa_family) {
-    case AF_INET:
-        g_snprintf(buf,
-                   len,
-                   "%s:%u",
-                   _nm_utils_inet4_ntop(sa->in.sin_addr.s_addr, s_addr),
-                   (guint) htons(sa->in.sin_port));
-        break;
-    case AF_INET6:
-        g_snprintf(buf,
-                   len,
-                   "[%s%s]:%u",
-                   _nm_utils_inet6_ntop(&sa->in6.sin6_addr, s_addr),
-                   (sa->in6.sin6_scope_id != 0
-                        ? nm_sprintf_buf(s_scope_id, "%u", sa->in6.sin6_scope_id)
-                        : ""),
-                   (guint) htons(sa->in6.sin6_port));
-        break;
-    case AF_UNSPEC:
-        g_snprintf(buf, len, "unspec");
-        break;
-    default:
-        g_snprintf(buf, len, "{addr-family:%u}", (unsigned) sa->sa.sa_family);
-        break;
-    }
-
-    return buf;
-}
-
-/*****************************************************************************/
-
-static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class;
-
-static void
-_idx_obj_id_hash_update(const NMDedupMultiIdxType *idx_type,
-                        const NMDedupMultiObj *    obj,
-                        NMHashState *              h)
-{
-    const NMPObject *o = (NMPObject *) obj;
-
-    nm_assert(idx_type && idx_type->klass == &_dedup_multi_idx_type_class);
-    nm_assert(NMP_OBJECT_GET_TYPE(o) != NMP_OBJECT_TYPE_UNKNOWN);
-
-    nmp_object_id_hash_update(o, h);
-}
-
-static gboolean
-_idx_obj_id_equal(const NMDedupMultiIdxType *idx_type,
-                  const NMDedupMultiObj *    obj_a,
-                  const NMDedupMultiObj *    obj_b)
-{
-    const NMPObject *o_a = (NMPObject *) obj_a;
-    const NMPObject *o_b = (NMPObject *) obj_b;
-
-    nm_assert(idx_type && idx_type->klass == &_dedup_multi_idx_type_class);
-    nm_assert(NMP_OBJECT_GET_TYPE(o_a) != NMP_OBJECT_TYPE_UNKNOWN);
-    nm_assert(NMP_OBJECT_GET_TYPE(o_b) != NMP_OBJECT_TYPE_UNKNOWN);
-
-    return nmp_object_id_equal(o_a, o_b);
-}
-
-static guint
-_idx_obj_part(const DedupMultiIdxType *idx_type,
-              const NMPObject *        obj_a,
-              const NMPObject *        obj_b,
-              NMHashState *            h)
-{
-    NMPObjectType obj_type;
-
-    /* the hash/equals functions are strongly related. So, keep them
-     * side-by-side and do it all in _idx_obj_part(). */
-
-    nm_assert(idx_type);
-    nm_assert(idx_type->parent.klass == &_dedup_multi_idx_type_class);
-    nm_assert(obj_a);
-    nm_assert(NMP_OBJECT_GET_TYPE(obj_a) != NMP_OBJECT_TYPE_UNKNOWN);
-    nm_assert(!obj_b || (NMP_OBJECT_GET_TYPE(obj_b) != NMP_OBJECT_TYPE_UNKNOWN));
-    nm_assert(!h || !obj_b);
-
-    switch (idx_type->cache_id_type) {
-    case NMP_CACHE_ID_TYPE_OBJECT_TYPE:
-        if (obj_b)
-            return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b);
-        if (h) {
-            nm_hash_update_vals(h, idx_type->cache_id_type, NMP_OBJECT_GET_TYPE(obj_a));
-        }
-        return 1;
-
-    case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME:
-        if (NMP_OBJECT_GET_TYPE(obj_a) != NMP_OBJECT_TYPE_LINK) {
-            /* first check, whether obj_a is suitable for this idx_type.
-             * If not, return 0 (which is correct for partitionable(), hash() and equal()
-             * functions. */
-            if (h)
-                nm_hash_update_val(h, obj_a);
-            return 0;
-        }
-        if (obj_b) {
-            /* we are in equal() mode. Compare obj_b with obj_a. */
-            return NMP_OBJECT_GET_TYPE(obj_b) == NMP_OBJECT_TYPE_LINK
-                   && nm_streq(obj_a->link.name, obj_b->link.name);
-        }
-        if (h) {
-            nm_hash_update_val(h, idx_type->cache_id_type);
-            nm_hash_update_strarr(h, obj_a->link.name);
-        }
-        /* just return 1, to indicate that obj_a is partitionable by this idx_type. */
-        return 1;
-
-    case NMP_CACHE_ID_TYPE_DEFAULT_ROUTES:
-        if (!NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_a),
-                       NMP_OBJECT_TYPE_IP4_ROUTE,
-                       NMP_OBJECT_TYPE_IP6_ROUTE)
-            || !NM_PLATFORM_IP_ROUTE_IS_DEFAULT(&obj_a->ip_route)
-            || !nmp_object_is_visible(obj_a)) {
-            if (h)
-                nm_hash_update_val(h, obj_a);
-            return 0;
-        }
-        if (obj_b) {
-            return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b)
-                   && NM_PLATFORM_IP_ROUTE_IS_DEFAULT(&obj_b->ip_route)
-                   && nmp_object_is_visible(obj_b);
-        }
-        if (h) {
-            nm_hash_update_vals(h, idx_type->cache_id_type, NMP_OBJECT_GET_TYPE(obj_a));
-        }
-        return 1;
-
-    case NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX:
-        if (!NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_a),
-                       NMP_OBJECT_TYPE_IP4_ADDRESS,
-                       NMP_OBJECT_TYPE_IP6_ADDRESS,
-                       NMP_OBJECT_TYPE_IP4_ROUTE,
-                       NMP_OBJECT_TYPE_IP6_ROUTE,
-                       NMP_OBJECT_TYPE_QDISC,
-                       NMP_OBJECT_TYPE_TFILTER)
-            || !nmp_object_is_visible(obj_a)) {
-            if (h)
-                nm_hash_update_val(h, obj_a);
-            return 0;
-        }
-        nm_assert(NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_a)->ifindex > 0);
-        if (obj_b) {
-            return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b)
-                   && NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_a)->ifindex
-                          == NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_b)->ifindex
-                   && nmp_object_is_visible(obj_b);
-        }
-        if (h) {
-            nm_hash_update_vals(h, idx_type->cache_id_type, obj_a->obj_with_ifindex.ifindex);
-        }
-        return 1;
-
-    case NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID:
-        obj_type = NMP_OBJECT_GET_TYPE(obj_a);
-        if (!NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)
-            || NMP_OBJECT_CAST_IP_ROUTE(obj_a)->ifindex <= 0) {
-            if (h)
-                nm_hash_update_val(h, obj_a);
-            return 0;
-        }
-        if (obj_b) {
-            return obj_type == NMP_OBJECT_GET_TYPE(obj_b)
-                   && NMP_OBJECT_CAST_IP_ROUTE(obj_b)->ifindex > 0
-                   && (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE
-                           ? (nm_platform_ip4_route_cmp(&obj_a->ip4_route,
-                                                        &obj_b->ip4_route,
-                                                        NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID)
-                              == 0)
-                           : (nm_platform_ip6_route_cmp(&obj_a->ip6_route,
-                                                        &obj_b->ip6_route,
-                                                        NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID)
-                              == 0));
-        }
-        if (h) {
-            nm_hash_update_val(h, idx_type->cache_id_type);
-            if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE)
-                nm_platform_ip4_route_hash_update(&obj_a->ip4_route,
-                                                  NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID,
-                                                  h);
-            else
-                nm_platform_ip6_route_hash_update(&obj_a->ip6_route,
-                                                  NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID,
-                                                  h);
-        }
-        return 1;
-
-    case NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY:
-        obj_type = NMP_OBJECT_GET_TYPE(obj_a);
-        /* currently, only routing rules are supported for this cache-id-type. */
-        if (obj_type != NMP_OBJECT_TYPE_ROUTING_RULE
-            || !NM_IN_SET(obj_a->routing_rule.addr_family, AF_INET, AF_INET6)) {
-            if (h)
-                nm_hash_update_val(h, obj_a);
-            return 0;
-        }
-        if (obj_b) {
-            return NMP_OBJECT_GET_TYPE(obj_b) == NMP_OBJECT_TYPE_ROUTING_RULE
-                   && obj_a->routing_rule.addr_family == obj_b->routing_rule.addr_family;
-        }
-        if (h) {
-            nm_hash_update_vals(h, idx_type->cache_id_type, obj_a->routing_rule.addr_family);
-        }
-        return 1;
-
-    case NMP_CACHE_ID_TYPE_NONE:
-    case __NMP_CACHE_ID_TYPE_MAX:
-        break;
-    }
-    nm_assert_not_reached();
-    return 0;
-}
-
-static gboolean
-_idx_obj_partitionable(const NMDedupMultiIdxType *idx_type, const NMDedupMultiObj *obj)
-{
-    return _idx_obj_part((DedupMultiIdxType *) idx_type, (NMPObject *) obj, NULL, NULL) != 0;
-}
-
-static void
-_idx_obj_partition_hash_update(const NMDedupMultiIdxType *idx_type,
-                               const NMDedupMultiObj *    obj,
-                               NMHashState *              h)
-{
-    _idx_obj_part((DedupMultiIdxType *) idx_type, (NMPObject *) obj, NULL, h);
-}
-
-static gboolean
-_idx_obj_partition_equal(const NMDedupMultiIdxType *idx_type,
-                         const NMDedupMultiObj *    obj_a,
-                         const NMDedupMultiObj *    obj_b)
-{
-    return _idx_obj_part((DedupMultiIdxType *) idx_type,
-                         (NMPObject *) obj_a,
-                         (NMPObject *) obj_b,
-                         NULL);
-}
-
-static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class = {
-    .idx_obj_id_hash_update        = _idx_obj_id_hash_update,
-    .idx_obj_id_equal              = _idx_obj_id_equal,
-    .idx_obj_partitionable         = _idx_obj_partitionable,
-    .idx_obj_partition_hash_update = _idx_obj_partition_hash_update,
-    .idx_obj_partition_equal       = _idx_obj_partition_equal,
-};
-
-static void
-_dedup_multi_idx_type_init(DedupMultiIdxType *idx_type, NMPCacheIdType cache_id_type)
-{
-    nm_dedup_multi_idx_type_init((NMDedupMultiIdxType *) idx_type, &_dedup_multi_idx_type_class);
-    idx_type->cache_id_type = cache_id_type;
-}
-
-/*****************************************************************************/
-
-static void
-_vlan_xgress_qos_mappings_hash_update(guint n_map, const NMVlanQosMapping *map, NMHashState *h)
-{
-    /* ensure no padding. */
-    G_STATIC_ASSERT(sizeof(NMVlanQosMapping) == 2 * sizeof(guint32));
-
-    nm_hash_update_val(h, n_map);
-    if (n_map)
-        nm_hash_update(h, map, n_map * sizeof(*map));
-}
-
-static int
-_vlan_xgress_qos_mappings_cmp(guint                   n_map,
-                              const NMVlanQosMapping *map1,
-                              const NMVlanQosMapping *map2)
-{
-    guint i;
-
-    for (i = 0; i < n_map; i++) {
-        if (map1[i].from != map2[i].from)
-            return map1[i].from < map2[i].from ? -1 : 1;
-        if (map1[i].to != map2[i].to)
-            return map1[i].to < map2[i].to ? -1 : 1;
-    }
-    return 0;
-}
-
-static void
-_vlan_xgress_qos_mappings_cpy(guint *                 dst_n_map,
-                              NMVlanQosMapping **     dst_map,
-                              guint                   src_n_map,
-                              const NMVlanQosMapping *src_map)
-{
-    if (src_n_map == 0) {
-        nm_clear_g_free(dst_map);
-        *dst_n_map = 0;
-    } else if (src_n_map != *dst_n_map
-               || _vlan_xgress_qos_mappings_cmp(src_n_map, *dst_map, src_map) != 0) {
-        nm_clear_g_free(dst_map);
-        *dst_n_map = src_n_map;
-        if (src_n_map > 0)
-            *dst_map = nm_memdup(src_map, sizeof(*src_map) * src_n_map);
-    }
-}
-
-/*****************************************************************************/
-
-static void
-_wireguard_allowed_ip_hash_update(const NMPWireGuardAllowedIP *ip, NMHashState *h)
-{
-    nm_hash_update_vals(h, ip->family, ip->mask);
-
-    if (ip->family == AF_INET)
-        nm_hash_update_val(h, ip->addr.addr4);
-    else if (ip->family == AF_INET6)
-        nm_hash_update_val(h, ip->addr.addr6);
-}
-
-static int
-_wireguard_allowed_ip_cmp(const NMPWireGuardAllowedIP *a, const NMPWireGuardAllowedIP *b)
-{
-    NM_CMP_SELF(a, b);
-
-    NM_CMP_FIELD(a, b, family);
-    NM_CMP_FIELD(a, b, mask);
-
-    if (a->family == AF_INET)
-        NM_CMP_FIELD(a, b, addr.addr4);
-    else if (a->family == AF_INET6)
-        NM_CMP_FIELD_IN6ADDR(a, b, addr.addr6);
-
-    return 0;
-}
-
-static void
-_wireguard_peer_hash_update(const NMPWireGuardPeer *peer, NMHashState *h)
-{
-    guint i;
-
-    nm_hash_update(h, peer->public_key, sizeof(peer->public_key));
-    nm_hash_update(h, peer->preshared_key, sizeof(peer->preshared_key));
-    nm_hash_update_vals(h,
-                        peer->persistent_keepalive_interval,
-                        peer->allowed_ips_len,
-                        peer->rx_bytes,
-                        peer->tx_bytes,
-                        peer->last_handshake_time.tv_sec,
-                        peer->last_handshake_time.tv_nsec);
-
-    nm_sock_addr_union_hash_update(&peer->endpoint, h);
-
-    for (i = 0; i < peer->allowed_ips_len; i++)
-        _wireguard_allowed_ip_hash_update(&peer->allowed_ips[i], h);
-}
-
-static int
-_wireguard_peer_cmp(const NMPWireGuardPeer *a, const NMPWireGuardPeer *b)
-{
-    guint i;
-
-    NM_CMP_SELF(a, b);
-
-    NM_CMP_FIELD(a, b, last_handshake_time.tv_sec);
-    NM_CMP_FIELD(a, b, last_handshake_time.tv_nsec);
-    NM_CMP_FIELD(a, b, rx_bytes);
-    NM_CMP_FIELD(a, b, tx_bytes);
-    NM_CMP_FIELD(a, b, allowed_ips_len);
-    NM_CMP_FIELD(a, b, persistent_keepalive_interval);
-    NM_CMP_FIELD(a, b, endpoint.sa.sa_family);
-    NM_CMP_FIELD_MEMCMP(a, b, public_key);
-    NM_CMP_FIELD_MEMCMP(a, b, preshared_key);
-
-    NM_CMP_RETURN(nm_sock_addr_union_cmp(&a->endpoint, &b->endpoint));
-
-    for (i = 0; i < a->allowed_ips_len; i++) {
-        NM_CMP_RETURN(_wireguard_allowed_ip_cmp(&a->allowed_ips[i], &b->allowed_ips[i]));
-    }
-
-    return 0;
-}
-
-/*****************************************************************************/
-
-static const char *
-_link_get_driver(struct udev_device *udevice, const char *kind, int ifindex)
-{
-    const char *driver = NULL;
-
-    nm_assert(kind == g_intern_string(kind));
-
-    if (udevice) {
-        driver = nmp_utils_udev_get_driver(udevice);
-        if (driver)
-            return driver;
-    }
-
-    if (kind)
-        return kind;
-
-    if (ifindex > 0) {
-        NMPUtilsEthtoolDriverInfo driver_info;
-
-        if (nmp_utils_ethtool_get_driver_info(ifindex, &driver_info)) {
-            if (driver_info.driver[0])
-                return g_intern_string(driver_info.driver);
-        }
-    }
-
-    return "unknown";
-}
-
-void
-_nmp_object_fixup_link_udev_fields(NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev)
-{
-    const char *driver      = NULL;
-    gboolean    initialized = FALSE;
-    NMPObject * obj;
-
-    nm_assert(obj_orig || *obj_new);
-    nm_assert(obj_new);
-    nm_assert(!obj_orig || NMP_OBJECT_GET_TYPE(obj_orig) == NMP_OBJECT_TYPE_LINK);
-    nm_assert(!*obj_new || NMP_OBJECT_GET_TYPE(*obj_new) == NMP_OBJECT_TYPE_LINK);
-
-    obj = *obj_new ?: obj_orig;
-
-    /* The link contains internal fields that are combined by
-     * properties from netlink and udev. Update those properties */
-
-    /* When a link is not in netlink, its udev fields don't matter. */
-    if (obj->_link.netlink.is_in_netlink) {
-        driver = _link_get_driver(obj->_link.udev.device, obj->link.kind, obj->link.ifindex);
-        if (obj->_link.udev.device)
-            initialized = TRUE;
-        else if (!use_udev) {
-            /* If we don't use udev, we immediately mark the link as initialized.
-             *
-             * For that, we consult @use_udev argument, that is cached via
-             * nmp_cache_use_udev_get(). It is on purpose not to test
-             * for a writable /sys on every call. A minor reason for that is
-             * performance, but the real reason is reproducibility.
-             * */
-            initialized = TRUE;
-        }
-    }
-
-    if (nm_streq0(obj->link.driver, driver) && obj->link.initialized == initialized)
-        return;
-
-    if (!*obj_new)
-        obj = *obj_new = nmp_object_clone(obj, FALSE);
-
-    obj->link.driver      = driver;
-    obj->link.initialized = initialized;
-}
-
-static void
-_nmp_object_fixup_link_master_connected(NMPObject **    obj_new,
-                                        NMPObject *     obj_orig,
-                                        const NMPCache *cache)
-{
-    NMPObject *obj;
-
-    nm_assert(obj_orig || *obj_new);
-    nm_assert(obj_new);
-    nm_assert(!obj_orig || NMP_OBJECT_GET_TYPE(obj_orig) == NMP_OBJECT_TYPE_LINK);
-    nm_assert(!*obj_new || NMP_OBJECT_GET_TYPE(*obj_new) == NMP_OBJECT_TYPE_LINK);
-
-    obj = *obj_new ?: obj_orig;
-
-    if (nmp_cache_link_connected_needs_toggle(cache, obj, NULL, NULL)) {
-        if (!*obj_new)
-            obj = *obj_new = nmp_object_clone(obj, FALSE);
-        obj->link.connected = !obj->link.connected;
-    }
-}
-
-/*****************************************************************************/
-
-static void
-_vt_cmd_obj_dispose_link(NMPObject *obj)
-{
-    if (obj->_link.udev.device) {
-        udev_device_unref(obj->_link.udev.device);
-        obj->_link.udev.device = NULL;
-    }
-    g_clear_object(&obj->_link.ext_data);
-    nmp_object_unref(obj->_link.netlink.lnk);
-}
-
-static void
-_vt_cmd_obj_dispose_lnk_vlan(NMPObject *obj)
-{
-    g_free((gpointer) obj->_lnk_vlan.ingress_qos_map);
-    g_free((gpointer) obj->_lnk_vlan.egress_qos_map);
-}
-
-static void
-_wireguard_clear(NMPObjectLnkWireGuard *lnk)
-{
-    guint i;
-
-    nm_explicit_bzero(lnk->_public.private_key, sizeof(lnk->_public.private_key));
-    for (i = 0; i < lnk->peers_len; i++) {
-        NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &lnk->peers[i];
-
-        nm_explicit_bzero(peer->preshared_key, sizeof(peer->preshared_key));
-    }
-    g_free((gpointer) lnk->peers);
-    g_free((gpointer) lnk->_allowed_ips_buf);
-}
-
-static void
-_vt_cmd_obj_dispose_lnk_wireguard(NMPObject *obj)
-{
-    _wireguard_clear(&obj->_lnk_wireguard);
-}
-
-static NMPObject *
-_nmp_object_new_from_class(const NMPClass *klass)
-{
-    NMPObject *obj;
-
-    nm_assert(klass);
-    nm_assert(klass->sizeof_data > 0);
-    nm_assert(klass->sizeof_public > 0 && klass->sizeof_public <= klass->sizeof_data);
-
-    obj         = g_slice_alloc0(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object));
-    obj->_class = klass;
-    obj->parent._ref_count = 1;
-    return obj;
-}
-
-NMPObject *
-nmp_object_new(NMPObjectType obj_type, gconstpointer plobj)
-{
-    const NMPClass *klass = nmp_class_from_type(obj_type);
-    NMPObject *     obj;
-
-    obj = _nmp_object_new_from_class(klass);
-    if (plobj)
-        memcpy(&obj->object, plobj, klass->sizeof_public);
-    return obj;
-}
-
-NMPObject *
-nmp_object_new_link(int ifindex)
-{
-    NMPObject *obj;
-
-    obj               = nmp_object_new(NMP_OBJECT_TYPE_LINK, NULL);
-    obj->link.ifindex = ifindex;
-    return obj;
-}
-
-/*****************************************************************************/
-
-static void
-_nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass)
-{
-    nm_assert(obj);
-    nm_assert(klass);
-
-    *obj = (NMPObject){
-        .parent =
-            {
-                .klass      = (const NMDedupMultiObjClass *) klass,
-                ._ref_count = NM_OBJ_REF_COUNT_STACKINIT,
-            },
-    };
-}
-
-static NMPObject *
-_nmp_object_stackinit_from_type(NMPObject *obj, NMPObjectType obj_type)
-{
-    const NMPClass *klass;
-
-    nm_assert(obj);
-    klass = nmp_class_from_type(obj_type);
-    nm_assert(klass);
-
-    *obj = (NMPObject){
-        .parent =
-            {
-                .klass      = (const NMDedupMultiObjClass *) klass,
-                ._ref_count = NM_OBJ_REF_COUNT_STACKINIT,
-            },
-    };
-    return obj;
-}
-
-const NMPObject *
-nmp_object_stackinit(NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj)
-{
-    const NMPClass *klass = nmp_class_from_type(obj_type);
-
-    _nmp_object_stackinit_from_class(obj, klass);
-    if (plobj)
-        memcpy(&obj->object, plobj, klass->sizeof_public);
-    return obj;
-}
-
-const NMPObject *
-nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src)
-{
-    const NMPClass *klass;
-
-    nm_assert(NMP_OBJECT_IS_VALID(src));
-    nm_assert(obj);
-
-    klass = NMP_OBJECT_GET_CLASS(src);
-    _nmp_object_stackinit_from_class(obj, klass);
-    if (klass->cmd_plobj_id_copy)
-        klass->cmd_plobj_id_copy(&obj->object, &src->object);
-    return obj;
-}
-
-const NMPObject *
-nmp_object_stackinit_id_link(NMPObject *obj, int ifindex)
-{
-    _nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_LINK);
-    obj->link.ifindex = ifindex;
-    return obj;
-}
-
-const NMPObject *
-nmp_object_stackinit_id_ip4_address(NMPObject *obj,
-                                    int        ifindex,
-                                    guint32    address,
-                                    guint8     plen,
-                                    guint32    peer_address)
-{
-    _nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP4_ADDRESS);
-    obj->ip4_address.ifindex      = ifindex;
-    obj->ip4_address.address      = address;
-    obj->ip4_address.plen         = plen;
-    obj->ip4_address.peer_address = peer_address;
-    return obj;
-}
-
-const NMPObject *
-nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in6_addr *address)
-{
-    _nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP6_ADDRESS);
-    obj->ip4_address.ifindex = ifindex;
-    if (address)
-        obj->ip6_address.address = *address;
-    return obj;
-}
-
-/*****************************************************************************/
-
-const char *
-nmp_object_to_string(const NMPObject *     obj,
-                     NMPObjectToStringMode to_string_mode,
-                     char *                buf,
-                     gsize                 buf_size)
-{
-    const NMPClass *klass;
-    char            buf2[sizeof(_nm_utils_to_string_buffer)];
-
-    if (!nm_utils_to_string_buffer_init_null(obj, &buf, &buf_size))
-        return buf;
-
-    g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj), NULL);
-
-    klass = NMP_OBJECT_GET_CLASS(obj);
-
-    if (klass->cmd_obj_to_string)
-        return klass->cmd_obj_to_string(obj, to_string_mode, buf, buf_size);
-
-    switch (to_string_mode) {
-    case NMP_OBJECT_TO_STRING_ID:
-        if (!klass->cmd_plobj_to_string_id) {
-            g_snprintf(buf, buf_size, "%p", obj);
-            return buf;
-        }
-        return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size);
-    case NMP_OBJECT_TO_STRING_ALL:
-        g_snprintf(
-            buf,
-            buf_size,
-            "[%s,%p,%u,%calive,%cvisible; %s]",
-            klass->obj_type_name,
-            obj,
-            obj->parent._ref_count,
-            nmp_object_is_alive(obj) ? '+' : '-',
-            nmp_object_is_visible(obj) ? '+' : '-',
-            NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf2, sizeof(buf2)));
-        return buf;
-    case NMP_OBJECT_TO_STRING_PUBLIC:
-        NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size);
-        return buf;
-    default:
-        g_return_val_if_reached("ERROR");
-    }
-}
-
-static const char *
-_vt_cmd_obj_to_string_link(const NMPObject *     obj,
-                           NMPObjectToStringMode to_string_mode,
-                           char *                buf,
-                           gsize                 buf_size)
-{
-    const NMPClass *klass = NMP_OBJECT_GET_CLASS(obj);
-    char *          b     = buf;
-
-    switch (to_string_mode) {
-    case NMP_OBJECT_TO_STRING_ID:
-        return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size);
-    case NMP_OBJECT_TO_STRING_ALL:
-        nm_utils_strbuf_append(&b,
-                               &buf_size,
-                               "[%s,%p,%u,%calive,%cvisible,%cin-nl,%p; ",
-                               klass->obj_type_name,
-                               obj,
-                               obj->parent._ref_count,
-                               nmp_object_is_alive(obj) ? '+' : '-',
-                               nmp_object_is_visible(obj) ? '+' : '-',
-                               obj->_link.netlink.is_in_netlink ? '+' : '-',
-                               obj->_link.udev.device);
-        NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size);
-        nm_utils_strbuf_seek_end(&b, &buf_size);
-        if (obj->_link.netlink.lnk) {
-            nm_utils_strbuf_append_str(&b, &buf_size, "; ");
-            nmp_object_to_string(obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_ALL, b, buf_size);
-            nm_utils_strbuf_seek_end(&b, &buf_size);
-        }
-        nm_utils_strbuf_append_c(&b, &buf_size, ']');
-        return buf;
-    case NMP_OBJECT_TO_STRING_PUBLIC:
-        NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size);
-        if (obj->_link.netlink.lnk) {
-            nm_utils_strbuf_seek_end(&b, &buf_size);
-            nm_utils_strbuf_append_str(&b, &buf_size, "; ");
-            nmp_object_to_string(obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_PUBLIC, b, buf_size);
-        }
-        return buf;
-    default:
-        g_return_val_if_reached("ERROR");
-    }
-}
-
-static const char *
-_vt_cmd_obj_to_string_lnk_vlan(const NMPObject *     obj,
-                               NMPObjectToStringMode to_string_mode,
-                               char *                buf,
-                               gsize                 buf_size)
-{
-    const NMPClass *klass;
-    char            buf2[sizeof(_nm_utils_to_string_buffer)];
-    char *          b;
-    gsize           l;
-
-    klass = NMP_OBJECT_GET_CLASS(obj);
-
-    switch (to_string_mode) {
-    case NMP_OBJECT_TO_STRING_ID:
-        g_snprintf(buf, buf_size, "%p", obj);
-        return buf;
-    case NMP_OBJECT_TO_STRING_ALL:
-
-        g_snprintf(buf,
-                   buf_size,
-                   "[%s,%p,%u,%calive,%cvisible; %s]",
-                   klass->obj_type_name,
-                   obj,
-                   obj->parent._ref_count,
-                   nmp_object_is_alive(obj) ? '+' : '-',
-                   nmp_object_is_visible(obj) ? '+' : '-',
-                   nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2)));
-        return buf;
-    case NMP_OBJECT_TO_STRING_PUBLIC:
-        NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size);
-
-        b = buf;
-        l = strlen(b);
-        b += l;
-        buf_size -= l;
-
-        if (obj->_lnk_vlan.n_ingress_qos_map) {
-            nm_platform_vlan_qos_mapping_to_string(" ingress-qos-map",
-                                                   obj->_lnk_vlan.ingress_qos_map,
-                                                   obj->_lnk_vlan.n_ingress_qos_map,
-                                                   b,
-                                                   buf_size);
-            l = strlen(b);
-            b += l;
-            buf_size -= l;
-        }
-        if (obj->_lnk_vlan.n_egress_qos_map) {
-            nm_platform_vlan_qos_mapping_to_string(" egress-qos-map",
-                                                   obj->_lnk_vlan.egress_qos_map,
-                                                   obj->_lnk_vlan.n_egress_qos_map,
-                                                   b,
-                                                   buf_size);
-            l = strlen(b);
-            b += l;
-            buf_size -= l;
-        }
-
-        return buf;
-    default:
-        g_return_val_if_reached("ERROR");
-    }
-}
-
-static const char *
-_vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *     obj,
-                                    NMPObjectToStringMode to_string_mode,
-                                    char *                buf,
-                                    gsize                 buf_size)
-{
-    const NMPClass *klass;
-    char            buf2[sizeof(_nm_utils_to_string_buffer)];
-    char *          b;
-    guint           i;
-
-    klass = NMP_OBJECT_GET_CLASS(obj);
-
-    switch (to_string_mode) {
-    case NMP_OBJECT_TO_STRING_ID:
-        g_snprintf(buf, buf_size, "%p", obj);
-        return buf;
-    case NMP_OBJECT_TO_STRING_ALL:
-        b = buf;
-
-        nm_utils_strbuf_append(
-            &b,
-            &buf_size,
-            "[%s,%p,%u,%calive,%cvisible; %s"
-            "%s",
-            klass->obj_type_name,
-            obj,
-            obj->parent._ref_count,
-            nmp_object_is_alive(obj) ? '+' : '-',
-            nmp_object_is_visible(obj) ? '+' : '-',
-            nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2)),
-            obj->_lnk_wireguard.peers_len > 0 ? " peers {" : "");
-
-        for (i = 0; i < obj->_lnk_wireguard.peers_len; i++) {
-            const NMPWireGuardPeer *peer = &obj->_lnk_wireguard.peers[i];
-
-            nm_utils_strbuf_append_str(&b, &buf_size, " { ");
-            nm_platform_wireguard_peer_to_string(peer, b, buf_size);
-            nm_utils_strbuf_seek_end(&b, &buf_size);
-            nm_utils_strbuf_append_str(&b, &buf_size, " }");
-        }
-        if (obj->_lnk_wireguard.peers_len)
-            nm_utils_strbuf_append_str(&b, &buf_size, " }");
-
-        return buf;
-    case NMP_OBJECT_TO_STRING_PUBLIC:
-        NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size);
-
-        return buf;
-    default:
-        g_return_val_if_reached("ERROR");
-    }
-}
-
-#define _vt_cmd_plobj_to_string_id(type, plat_type, ...)                                  \
-    static const char *_vt_cmd_plobj_to_string_id_##type(const NMPlatformObject *_obj,    \
-                                                         char *                  buf,     \
-                                                         gsize                   buf_len) \
-    {                                                                                     \
-        plat_type *const obj = (plat_type *) _obj;                                        \
-        _nm_unused char  buf1[NM_UTILS_INET_ADDRSTRLEN];                                  \
-        _nm_unused char  buf2[NM_UTILS_INET_ADDRSTRLEN];                                  \
-                                                                                          \
-        g_snprintf(buf, buf_len, __VA_ARGS__);                                            \
-        return buf;                                                                       \
-    }                                                                                     \
-    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
-
-_vt_cmd_plobj_to_string_id(link, NMPlatformLink, "%d", obj->ifindex);
-
-_vt_cmd_plobj_to_string_id(ip4_address,
-                           NMPlatformIP4Address,
-                           "%d: %s/%d%s%s",
-                           obj->ifindex,
-                           _nm_utils_inet4_ntop(obj->address, buf1),
-                           obj->plen,
-                           obj->peer_address != obj->address ? "," : "",
-                           obj->peer_address != obj->address ? _nm_utils_inet4_ntop(
-                               nm_utils_ip4_address_clear_host_address(obj->peer_address,
-                                                                       obj->plen),
-                               buf2)
-                                                             : "");
-
-_vt_cmd_plobj_to_string_id(ip6_address,
-                           NMPlatformIP6Address,
-                           "%d: %s",
-                           obj->ifindex,
-                           _nm_utils_inet6_ntop(&obj->address, buf1));
-
-_vt_cmd_plobj_to_string_id(qdisc, NMPlatformQdisc, "%d: %d", obj->ifindex, obj->parent);
-
-_vt_cmd_plobj_to_string_id(tfilter, NMPlatformTfilter, "%d: %d", obj->ifindex, obj->parent);
-
-void
-nmp_object_hash_update(const NMPObject *obj, NMHashState *h)
-{
-    const NMPClass *klass;
-
-    g_return_if_fail(NMP_OBJECT_IS_VALID(obj));
-
-    klass = NMP_OBJECT_GET_CLASS(obj);
-
-    nm_hash_update_val(h, klass->obj_type);
-    if (klass->cmd_obj_hash_update)
-        klass->cmd_obj_hash_update(obj, h);
-    else if (klass->cmd_plobj_hash_update)
-        klass->cmd_plobj_hash_update(&obj->object, h);
-    else
-        nm_hash_update_val(h, obj);
-}
-
-static void
-_vt_cmd_obj_hash_update_link(const NMPObject *obj, NMHashState *h)
-{
-    nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK);
-
-    nm_platform_link_hash_update(&obj->link, h);
-    nm_hash_update_vals(h,
-                        obj->_link.netlink.is_in_netlink,
-                        obj->_link.wireguard_family_id,
-                        obj->_link.udev.device);
-    if (obj->_link.netlink.lnk)
-        nmp_object_hash_update(obj->_link.netlink.lnk, h);
-}
-
-static void
-_vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, NMHashState *h)
-{
-    nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_VLAN);
-
-    nm_platform_lnk_vlan_hash_update(&obj->lnk_vlan, h);
-    _vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_ingress_qos_map,
-                                          obj->_lnk_vlan.ingress_qos_map,
-                                          h);
-    _vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_egress_qos_map,
-                                          obj->_lnk_vlan.egress_qos_map,
-                                          h);
-}
-
-static void
-_vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, NMHashState *h)
-{
-    guint i;
-
-    nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_WIREGUARD);
-
-    nm_platform_lnk_wireguard_hash_update(&obj->lnk_wireguard, h);
-
-    nm_hash_update_val(h, obj->_lnk_wireguard.peers_len);
-    for (i = 0; i < obj->_lnk_wireguard.peers_len; i++)
-        _wireguard_peer_hash_update(&obj->_lnk_wireguard.peers[i], h);
-}
-
-int
-nmp_object_cmp(const NMPObject *obj1, const NMPObject *obj2)
-{
-    const NMPClass *klass1, *klass2;
-
-    NM_CMP_SELF(obj1, obj2);
-
-    g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), -1);
-    g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), 1);
-
-    klass1 = NMP_OBJECT_GET_CLASS(obj1);
-    klass2 = NMP_OBJECT_GET_CLASS(obj2);
-
-    if (klass1 != klass2) {
-        nm_assert(klass1->obj_type != klass2->obj_type);
-        return klass1->obj_type < klass2->obj_type ? -1 : 1;
-    }
-
-    if (klass1->cmd_obj_cmp)
-        return klass1->cmd_obj_cmp(obj1, obj2);
-    return klass1->cmd_plobj_cmp(&obj1->object, &obj2->object);
-}
-
-static int
-_vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2)
-{
-    NM_CMP_RETURN(nm_platform_link_cmp(&obj1->link, &obj2->link));
-    NM_CMP_DIRECT(obj1->_link.netlink.is_in_netlink, obj2->_link.netlink.is_in_netlink);
-    NM_CMP_RETURN(nmp_object_cmp(obj1->_link.netlink.lnk, obj2->_link.netlink.lnk));
-    NM_CMP_DIRECT(obj1->_link.wireguard_family_id, obj2->_link.wireguard_family_id);
-
-    if (obj1->_link.udev.device != obj2->_link.udev.device) {
-        if (!obj1->_link.udev.device)
-            return -1;
-        if (!obj2->_link.udev.device)
-            return 1;
-
-        /* Only compare based on pointer values. That is ugly because it's not a
-         * stable sort order.
-         *
-         * Have this check as very last. */
-        return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1;
-    }
-
-    return 0;
-}
-
-static int
-_vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2)
-{
-    int c;
-
-    c = nm_platform_lnk_vlan_cmp(&obj1->lnk_vlan, &obj2->lnk_vlan);
-    if (c)
-        return c;
-
-    if (obj1->_lnk_vlan.n_ingress_qos_map != obj2->_lnk_vlan.n_ingress_qos_map)
-        return obj1->_lnk_vlan.n_ingress_qos_map < obj2->_lnk_vlan.n_ingress_qos_map ? -1 : 1;
-    if (obj1->_lnk_vlan.n_egress_qos_map != obj2->_lnk_vlan.n_egress_qos_map)
-        return obj1->_lnk_vlan.n_egress_qos_map < obj2->_lnk_vlan.n_egress_qos_map ? -1 : 1;
-
-    c = _vlan_xgress_qos_mappings_cmp(obj1->_lnk_vlan.n_ingress_qos_map,
-                                      obj1->_lnk_vlan.ingress_qos_map,
-                                      obj2->_lnk_vlan.ingress_qos_map);
-    if (c)
-        return c;
-    c = _vlan_xgress_qos_mappings_cmp(obj1->_lnk_vlan.n_egress_qos_map,
-                                      obj1->_lnk_vlan.egress_qos_map,
-                                      obj2->_lnk_vlan.egress_qos_map);
-
-    return c;
-}
-
-static int
-_vt_cmd_obj_cmp_lnk_wireguard(const NMPObject *obj1, const NMPObject *obj2)
-{
-    guint i;
-
-    NM_CMP_RETURN(nm_platform_lnk_wireguard_cmp(&obj1->lnk_wireguard, &obj2->lnk_wireguard));
-
-    NM_CMP_FIELD(obj1, obj2, _lnk_wireguard.peers_len);
-
-    for (i = 0; i < obj1->_lnk_wireguard.peers_len; i++)
-        NM_CMP_RETURN(
-            _wireguard_peer_cmp(&obj1->_lnk_wireguard.peers[i], &obj2->_lnk_wireguard.peers[i]));
-
-    return 0;
-}
-
-/* @src is a const object, which is not entirely correct for link types, where
- * we increase the ref count for src->_link.udev.device.
- * Hence, nmp_object_copy() can violate the const promise of @src.
- * */
-void
-nmp_object_copy(NMPObject *dst, const NMPObject *src, gboolean id_only)
-{
-    g_return_if_fail(NMP_OBJECT_IS_VALID(dst));
-    g_return_if_fail(NMP_OBJECT_IS_VALID(src));
-    g_return_if_fail(!NMP_OBJECT_IS_STACKINIT(dst));
-
-    if (src != dst) {
-        const NMPClass *klass = NMP_OBJECT_GET_CLASS(dst);
-
-        g_return_if_fail(klass == NMP_OBJECT_GET_CLASS(src));
-
-        if (id_only) {
-            if (klass->cmd_plobj_id_copy)
-                klass->cmd_plobj_id_copy(&dst->object, &src->object);
-        } else if (klass->cmd_obj_copy)
-            klass->cmd_obj_copy(dst, src);
-        else
-            memcpy(&dst->object, &src->object, klass->sizeof_data);
-    }
-}
-
-static void
-_vt_cmd_obj_copy_link(NMPObject *dst, const NMPObject *src)
-{
-    if (dst->_link.udev.device != src->_link.udev.device) {
-        if (src->_link.udev.device)
-            udev_device_ref(src->_link.udev.device);
-        if (dst->_link.udev.device)
-            udev_device_unref(dst->_link.udev.device);
-        dst->_link.udev.device = src->_link.udev.device;
-    }
-    if (dst->_link.netlink.lnk != src->_link.netlink.lnk) {
-        if (src->_link.netlink.lnk)
-            nmp_object_ref(src->_link.netlink.lnk);
-        if (dst->_link.netlink.lnk)
-            nmp_object_unref(dst->_link.netlink.lnk);
-        dst->_link.netlink.lnk = src->_link.netlink.lnk;
-    }
-    if (dst->_link.ext_data != src->_link.ext_data) {
-        if (dst->_link.ext_data)
-            g_clear_object(&dst->_link.ext_data);
-        if (src->_link.ext_data)
-            dst->_link.ext_data = g_object_ref(src->_link.ext_data);
-    }
-    dst->_link = src->_link;
-}
-
-static void
-_vt_cmd_obj_copy_lnk_vlan(NMPObject *dst, const NMPObject *src)
-{
-    dst->lnk_vlan = src->lnk_vlan;
-    _vlan_xgress_qos_mappings_cpy(
-        &dst->_lnk_vlan.n_ingress_qos_map,
-        NM_UNCONST_PPTR(NMVlanQosMapping, &dst->_lnk_vlan.ingress_qos_map),
-        src->_lnk_vlan.n_ingress_qos_map,
-        src->_lnk_vlan.ingress_qos_map);
-    _vlan_xgress_qos_mappings_cpy(&dst->_lnk_vlan.n_egress_qos_map,
-                                  NM_UNCONST_PPTR(NMVlanQosMapping, &dst->_lnk_vlan.egress_qos_map),
-                                  src->_lnk_vlan.n_egress_qos_map,
-                                  src->_lnk_vlan.egress_qos_map);
-}
-
-static void
-_vt_cmd_obj_copy_lnk_wireguard(NMPObject *dst, const NMPObject *src)
-{
-    guint i;
-
-    nm_assert(dst != src);
-
-    _wireguard_clear(&dst->_lnk_wireguard);
-
-    dst->_lnk_wireguard = src->_lnk_wireguard;
-
-    dst->_lnk_wireguard.peers = nm_memdup(dst->_lnk_wireguard.peers,
-                                          sizeof(NMPWireGuardPeer) * dst->_lnk_wireguard.peers_len);
-    dst->_lnk_wireguard._allowed_ips_buf =
-        nm_memdup(dst->_lnk_wireguard._allowed_ips_buf,
-                  sizeof(NMPWireGuardAllowedIP) * dst->_lnk_wireguard._allowed_ips_buf_len);
-
-    /* all the peers' pointers point into the buffer. They need to be readjusted. */
-    for (i = 0; i < dst->_lnk_wireguard.peers_len; i++) {
-        NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &dst->_lnk_wireguard.peers[i];
-
-        if (peer->allowed_ips_len == 0) {
-            nm_assert(!peer->allowed_ips);
-            continue;
-        }
-        nm_assert(dst->_lnk_wireguard._allowed_ips_buf_len > 0);
-        nm_assert(src->_lnk_wireguard._allowed_ips_buf);
-        nm_assert(peer->allowed_ips >= src->_lnk_wireguard._allowed_ips_buf);
-        nm_assert(
-            &peer->allowed_ips[peer->allowed_ips_len]
-            <= &src->_lnk_wireguard._allowed_ips_buf[src->_lnk_wireguard._allowed_ips_buf_len]);
-
-        peer->allowed_ips =
-            &dst->_lnk_wireguard
-                 ._allowed_ips_buf[peer->allowed_ips - src->_lnk_wireguard._allowed_ips_buf];
-    }
-
-    nm_assert(nmp_object_equal(src, dst));
-}
-
-#define _vt_cmd_plobj_id_copy(type, plat_type, cmd)                                                \
-    static void _vt_cmd_plobj_id_copy_##type(NMPlatformObject *_dst, const NMPlatformObject *_src) \
-    {                                                                                              \
-        plat_type *const       dst = (plat_type *) _dst;                                           \
-        const plat_type *const src = (const plat_type *) _src;                                     \
-        {                                                                                          \
-            cmd                                                                                    \
-        }                                                                                          \
-    }                                                                                              \
-    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
-
-_vt_cmd_plobj_id_copy(link, NMPlatformLink, { dst->ifindex = src->ifindex; });
-
-_vt_cmd_plobj_id_copy(ip4_address, NMPlatformIP4Address, {
-    dst->ifindex      = src->ifindex;
-    dst->plen         = src->plen;
-    dst->address      = src->address;
-    dst->peer_address = src->peer_address;
-});
-
-_vt_cmd_plobj_id_copy(ip6_address, NMPlatformIP6Address, {
-    dst->ifindex = src->ifindex;
-    dst->address = src->address;
-});
-
-_vt_cmd_plobj_id_copy(ip4_route, NMPlatformIP4Route, {
-    *dst = *src;
-    nm_assert(nm_platform_ip4_route_cmp(dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0);
-});
-
-_vt_cmd_plobj_id_copy(ip6_route, NMPlatformIP6Route, {
-    *dst = *src;
-    nm_assert(nm_platform_ip6_route_cmp(dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0);
-});
-
-_vt_cmd_plobj_id_copy(routing_rule, NMPlatformRoutingRule, {
-    *dst = *src;
-    nm_assert(nm_platform_routing_rule_cmp(dst, src, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0);
-});
-
-/* Uses internally nmp_object_copy(), hence it also violates the const
- * promise for @obj.
- * */
-NMPObject *
-nmp_object_clone(const NMPObject *obj, gboolean id_only)
-{
-    NMPObject *dst;
-
-    if (!obj)
-        return NULL;
-
-    g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj), NULL);
-
-    dst = _nmp_object_new_from_class(NMP_OBJECT_GET_CLASS(obj));
-    nmp_object_copy(dst, obj, id_only);
-    return dst;
-}
-
-int
-nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2)
-{
-    const NMPClass *klass, *klass2;
-
-    NM_CMP_SELF(obj1, obj2);
-
-    g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), FALSE);
-    g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), FALSE);
-
-    klass = NMP_OBJECT_GET_CLASS(obj1);
-    nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp);
-
-    klass2 = NMP_OBJECT_GET_CLASS(obj2);
-    nm_assert(klass);
-    if (klass != klass2) {
-        nm_assert(klass2);
-        NM_CMP_DIRECT(klass->obj_type, klass2->obj_type);
-        /* resort to pointer comparison */
-        NM_CMP_DIRECT_PTR(klass, klass2);
-        return 0;
-    }
-
-    if (!klass->cmd_plobj_id_cmp) {
-        /* the klass doesn't implement ID cmp(). That means, different objects
-         * never compare equal, but the cmp() according to their pointer value. */
-        NM_CMP_DIRECT_PTR(obj1, obj2);
-        return 0;
-    }
-
-    return klass->cmd_plobj_id_cmp(&obj1->object, &obj2->object);
-}
-
-#define _vt_cmd_plobj_id_cmp(type, plat_type, cmd)                        \
-    static int _vt_cmd_plobj_id_cmp_##type(const NMPlatformObject *_obj1, \
-                                           const NMPlatformObject *_obj2) \
-    {                                                                     \
-        const plat_type *const obj1 = (const plat_type *) _obj1;          \
-        const plat_type *const obj2 = (const plat_type *) _obj2;          \
-                                                                          \
-        NM_CMP_SELF(obj1, obj2);                                          \
-        {                                                                 \
-            cmd;                                                          \
-        }                                                                 \
-        return 0;                                                         \
-    }                                                                     \
-    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
-
-_vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); });
-
-_vt_cmd_plobj_id_cmp(ip4_address, NMPlatformIP4Address, {
-    NM_CMP_FIELD(obj1, obj2, ifindex);
-    NM_CMP_FIELD(obj1, obj2, plen);
-    NM_CMP_FIELD(obj1, obj2, address);
-    /* for IPv4 addresses, you can add the same local address with differing peer-address
-     * (IFA_ADDRESS), provided that their net-part differs. */
-    NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(obj1->peer_address, obj2->peer_address, obj1->plen);
-});
-
-_vt_cmd_plobj_id_cmp(ip6_address, NMPlatformIP6Address, {
-    NM_CMP_FIELD(obj1, obj2, ifindex);
-    /* for IPv6 addresses, the prefix length is not part of the primary identifier. */
-    NM_CMP_FIELD_IN6ADDR(obj1, obj2, address);
-});
-
-_vt_cmd_plobj_id_cmp(qdisc, NMPlatformQdisc, {
-    NM_CMP_FIELD(obj1, obj2, ifindex);
-    NM_CMP_FIELD(obj1, obj2, parent);
-});
-
-_vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, {
-    NM_CMP_FIELD(obj1, obj2, ifindex);
-    NM_CMP_FIELD(obj1, obj2, handle);
-});
-
-static int
-_vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
-{
-    return nm_platform_ip4_route_cmp((NMPlatformIP4Route *) obj1,
-                                     (NMPlatformIP4Route *) obj2,
-                                     NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID);
-}
-
-static int
-_vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
-{
-    return nm_platform_ip6_route_cmp((NMPlatformIP6Route *) obj1,
-                                     (NMPlatformIP6Route *) obj2,
-                                     NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID);
-}
-
-static int
-_vt_cmd_plobj_id_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
-{
-    return nm_platform_routing_rule_cmp((NMPlatformRoutingRule *) obj1,
-                                        (NMPlatformRoutingRule *) obj2,
-                                        NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID);
-}
-
-void
-nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h)
-{
-    const NMPClass *klass;
-
-    g_return_if_fail(NMP_OBJECT_IS_VALID(obj));
-
-    klass = NMP_OBJECT_GET_CLASS(obj);
-
-    nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp);
-
-    if (!klass->cmd_plobj_id_hash_update) {
-        /* The klass doesn't implement ID compare. It means, to use pointer
-         * equality. */
-        nm_hash_update_val(h, obj);
-        return;
-    }
-
-    nm_hash_update_val(h, klass->obj_type);
-    klass->cmd_plobj_id_hash_update(&obj->object, h);
-}
-
-guint
-nmp_object_id_hash(const NMPObject *obj)
-{
-    NMHashState h;
-
-    if (!obj)
-        return nm_hash_static(914932607u);
-
-    nm_hash_init(&h, 914932607u);
-    nmp_object_id_hash_update(obj, &h);
-    return nm_hash_complete(&h);
-}
-
-#define _vt_cmd_plobj_id_hash_update(type, plat_type, cmd)                                        \
-    static void _vt_cmd_plobj_id_hash_update_##type(const NMPlatformObject *_obj, NMHashState *h) \
-    {                                                                                             \
-        const plat_type *const obj = (const plat_type *) _obj;                                    \
-        {                                                                                         \
-            cmd;                                                                                  \
-        }                                                                                         \
-    }                                                                                             \
-    _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
-
-_vt_cmd_plobj_id_hash_update(link, NMPlatformLink, { nm_hash_update_val(h, obj->ifindex); });
-
-_vt_cmd_plobj_id_hash_update(ip4_address, NMPlatformIP4Address, {
-    nm_hash_update_vals(
-        h,
-        obj->ifindex,
-        obj->plen,
-        obj->address,
-        /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */
-        nm_utils_ip4_address_clear_host_address(obj->peer_address, obj->plen));
-});
-
-_vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, {
-    nm_hash_update_vals(
-        h,
-        obj->ifindex,
-        /* for IPv6 addresses, the prefix length is not part of the primary identifier. */
-        obj->address);
-});
-
-_vt_cmd_plobj_id_hash_update(ip4_route, NMPlatformIP4Route, {
-    nm_platform_ip4_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h);
-});
-
-_vt_cmd_plobj_id_hash_update(ip6_route, NMPlatformIP6Route, {
-    nm_platform_ip6_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h);
-});
-
-_vt_cmd_plobj_id_hash_update(routing_rule, NMPlatformRoutingRule, {
-    nm_platform_routing_rule_hash_update(obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, h);
-});
-
-_vt_cmd_plobj_id_hash_update(qdisc, NMPlatformQdisc, {
-    nm_hash_update_vals(h, obj->ifindex, obj->parent);
-});
-
-_vt_cmd_plobj_id_hash_update(tfilter, NMPlatformTfilter, {
-    nm_hash_update_vals(h, obj->ifindex, obj->handle);
-});
-
-static void
-_vt_cmd_plobj_hash_update_ip4_route(const NMPlatformObject *obj, NMHashState *h)
-{
-    return nm_platform_ip4_route_hash_update((const NMPlatformIP4Route *) obj,
-                                             NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL,
-                                             h);
-}
-
-static void
-_vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h)
-{
-    return nm_platform_ip6_route_hash_update((const NMPlatformIP6Route *) obj,
-                                             NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL,
-                                             h);
-}
-
-static void
-_vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState *h)
-{
-    return nm_platform_routing_rule_hash_update((const NMPlatformRoutingRule *) obj,
-                                                NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL,
-                                                h);
-}
-
-guint
-nmp_object_indirect_id_hash(gconstpointer a)
-{
-    const NMPObject *const *p_obj = a;
-
-    return nmp_object_id_hash(*p_obj);
-}
-
-gboolean
-nmp_object_indirect_id_equal(gconstpointer a, gconstpointer b)
-{
-    const NMPObject *const *p_obj_a = a;
-    const NMPObject *const *p_obj_b = b;
-
-    return nmp_object_id_equal(*p_obj_a, *p_obj_b);
-}
-
-/*****************************************************************************/
-
-gboolean
-nmp_object_is_alive(const NMPObject *obj)
-{
-    const NMPClass *klass;
-
-    /* for convenience, allow NULL. */
-    if (!obj)
-        return FALSE;
-
-    klass = NMP_OBJECT_GET_CLASS(obj);
-    return !klass->cmd_obj_is_alive || klass->cmd_obj_is_alive(obj);
-}
-
-static gboolean
-_vt_cmd_obj_is_alive_link(const NMPObject *obj)
-{
-    return NMP_OBJECT_CAST_LINK(obj)->ifindex > 0
-           && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device);
-}
-
-static gboolean
-_vt_cmd_obj_is_alive_ipx_address(const NMPObject *obj)
-{
-    return NMP_OBJECT_CAST_IP_ADDRESS(obj)->ifindex > 0;
-}
-
-static gboolean
-_vt_cmd_obj_is_alive_ipx_route(const NMPObject *obj)
-{
-    /* We want to ignore routes that are RTM_F_CLONED but we still
-     * let nmp_object_from_nl() create such route objects, instead of
-     * returning NULL right away.
-     *
-     * The idea is, that if we have the same route (according to its id)
-     * in the cache with !RTM_F_CLONED, an update that changes the route
-     * to be RTM_F_CLONED must remove the instance.
-     *
-     * If nmp_object_from_nl() would just return NULL, we couldn't look
-     * into the cache to see if it contains a route that now disappears
-     * (because it changed to be cloned).
-     *
-     * Instead we create a dead object, and nmp_cache_update_netlink()
-     * will remove the old version of the update.
-     **/
-    return NMP_OBJECT_CAST_IP_ROUTE(obj)->ifindex > 0
-           && !NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, RTM_F_CLONED);
-}
-
-static gboolean
-_vt_cmd_obj_is_alive_routing_rule(const NMPObject *obj)
-{
-    return NM_IN_SET(obj->routing_rule.addr_family, AF_INET, AF_INET6);
-}
-
-static gboolean
-_vt_cmd_obj_is_alive_qdisc(const NMPObject *obj)
-{
-    return NMP_OBJECT_CAST_QDISC(obj)->ifindex > 0;
-}
-
-static gboolean
-_vt_cmd_obj_is_alive_tfilter(const NMPObject *obj)
-{
-    return NMP_OBJECT_CAST_TFILTER(obj)->ifindex > 0;
-}
-
-gboolean
-nmp_object_is_visible(const NMPObject *obj)
-{
-    const NMPClass *klass;
-
-    /* for convenience, allow NULL. */
-    if (!obj)
-        return FALSE;
-
-    klass = NMP_OBJECT_GET_CLASS(obj);
-
-    /* a dead object is never visible. */
-    if (klass->cmd_obj_is_alive && !klass->cmd_obj_is_alive(obj))
-        return FALSE;
-
-    return !klass->cmd_obj_is_visible || klass->cmd_obj_is_visible(obj);
-}
-
-static gboolean
-_vt_cmd_obj_is_visible_link(const NMPObject *obj)
-{
-    return obj->_link.netlink.is_in_netlink && obj->link.name[0];
-}
-
-/*****************************************************************************/
-
-static const guint8 _supported_cache_ids_object[] = {
-    NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-    NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
-    0,
-};
-
-static const guint8 _supported_cache_ids_link[] = {
-    NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-    NMP_CACHE_ID_TYPE_LINK_BY_IFNAME,
-    0,
-};
-
-static const guint8 _supported_cache_ids_ipx_address[] = {
-    NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-    NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
-    0,
-};
-
-static const guint8 _supported_cache_ids_ipx_route[] = {
-    NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-    NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
-    NMP_CACHE_ID_TYPE_DEFAULT_ROUTES,
-    NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
-    0,
-};
-
-static const guint8 _supported_cache_ids_routing_rules[] = {
-    NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-    NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY,
-    0,
-};
-
-/*****************************************************************************/
-
-static void
-_vt_dedup_obj_destroy(NMDedupMultiObj *obj)
-{
-    NMPObject *     o = (NMPObject *) obj;
-    const NMPClass *klass;
-
-    nm_assert(o->parent._ref_count == 0);
-    nm_assert(!o->parent._multi_idx);
-
-    klass = o->_class;
-    if (klass->cmd_obj_dispose)
-        klass->cmd_obj_dispose(o);
-    g_slice_free1(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object), o);
-}
-
-static const NMDedupMultiObj *
-_vt_dedup_obj_clone(const NMDedupMultiObj *obj)
-{
-    return (const NMDedupMultiObj *) nmp_object_clone((const NMPObject *) obj, FALSE);
-}
-
-#define DEDUP_MULTI_OBJ_CLASS_INIT()                                                       \
-    {                                                                                      \
-        .obj_clone = _vt_dedup_obj_clone, .obj_destroy = _vt_dedup_obj_destroy,            \
-        .obj_full_hash_update =                                                            \
-            (void (*)(const NMDedupMultiObj *obj, NMHashState *h)) nmp_object_hash_update, \
-        .obj_full_equal = (gboolean(*)(const NMDedupMultiObj *obj_a,                       \
-                                       const NMDedupMultiObj *obj_b)) nmp_object_equal,    \
-    }
-
-/*****************************************************************************/
-
-static NMDedupMultiIdxType *
-_idx_type_get(const NMPCache *cache, NMPCacheIdType cache_id_type)
-{
-    nm_assert(cache);
-    nm_assert(cache_id_type > NMP_CACHE_ID_TYPE_NONE);
-    nm_assert(cache_id_type <= NMP_CACHE_ID_TYPE_MAX);
-    nm_assert((int) cache_id_type - 1 >= 0);
-    nm_assert((int) cache_id_type - 1 < G_N_ELEMENTS(cache->idx_types));
-
-    return (NMDedupMultiIdxType *) &cache->idx_types[cache_id_type - 1];
-}
-
-gboolean
-nmp_cache_use_udev_get(const NMPCache *cache)
-{
-    g_return_val_if_fail(cache, TRUE);
-
-    return cache->use_udev;
-}
-
-/*****************************************************************************/
-
-gboolean
-nmp_cache_link_connected_for_slave(int ifindex_master, const NMPObject *slave)
-{
-    nm_assert(NMP_OBJECT_GET_TYPE(slave) == NMP_OBJECT_TYPE_LINK);
-
-    return ifindex_master > 0 && slave->link.master == ifindex_master && slave->link.connected
-           && nmp_object_is_visible(slave);
-}
-
-/**
- * nmp_cache_link_connected_needs_toggle:
- * @cache: the platform cache
- * @master: the link object, that is checked whether its connected property
- *   needs to be toggled.
- * @potential_slave: (allow-none): an additional link object that is treated
- *   as if it was inside @cache. If given, it shaddows a link in the cache
- *   with the same ifindex.
- * @ignore_slave: (allow-none): if set, the check will pretend that @ignore_slave
- *   is not in the cache.
- *
- * NMPlatformLink has two connected flags: (master->link.flags&IFF_LOWER_UP) (as reported
- * from netlink) and master->link.connected. For bond and bridge master, kernel reports
- * those links as IFF_LOWER_UP if they have no slaves attached. We want to present instead
- * a combined @connected flag that shows masters without slaves as down.
- *
- * Check if the connected flag of @master should be toggled according to the content
- * of @cache (including @potential_slave).
- *
- * Returns: %TRUE, if @master->link.connected should be flipped/toggled.
- **/
-gboolean
-nmp_cache_link_connected_needs_toggle(const NMPCache * cache,
-                                      const NMPObject *master,
-                                      const NMPObject *potential_slave,
-                                      const NMPObject *ignore_slave)
-{
-    gboolean is_lower_up = FALSE;
-
-    if (!master || NMP_OBJECT_GET_TYPE(master) != NMP_OBJECT_TYPE_LINK || master->link.ifindex <= 0
-        || !nmp_object_is_visible(master)
-        || !NM_IN_SET(master->link.type, NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND))
-        return FALSE;
-
-    /* if native IFF_LOWER_UP is down, link.connected must also be down
-     * regardless of the slaves. */
-    if (!NM_FLAGS_HAS(master->link.n_ifi_flags, IFF_LOWER_UP))
-        return !!master->link.connected;
-
-    if (potential_slave && NMP_OBJECT_GET_TYPE(potential_slave) != NMP_OBJECT_TYPE_LINK)
-        potential_slave = NULL;
-
-    if (potential_slave
-        && nmp_cache_link_connected_for_slave(master->link.ifindex, potential_slave))
-        is_lower_up = TRUE;
-    else {
-        NMPLookup             lookup;
-        NMDedupMultiIter      iter;
-        const NMPlatformLink *link = NULL;
-
-        nmp_cache_iter_for_each_link (
-            &iter,
-            nmp_cache_lookup(cache, nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK)),
-            &link) {
-            const NMPObject *obj = NMP_OBJECT_UP_CAST((NMPlatformObject *) link);
-
-            if ((!potential_slave || potential_slave->link.ifindex != link->ifindex)
-                && ignore_slave != obj
-                && nmp_cache_link_connected_for_slave(master->link.ifindex, obj)) {
-                is_lower_up = TRUE;
-                break;
-            }
-        }
-    }
-    return !!master->link.connected != is_lower_up;
-}
-
-/**
- * nmp_cache_link_connected_needs_toggle_by_ifindex:
- * @cache:
- * @master_ifindex: the ifindex of a potential master that should be checked
- *   whether it needs toggling.
- * @potential_slave: (allow-none): passed to nmp_cache_link_connected_needs_toggle().
- *   It considers @potential_slave as being inside the cache, replacing an existing
- *   link with the same ifindex.
- * @ignore_slave: (allow-onne): passed to nmp_cache_link_connected_needs_toggle().
- *
- * The flag obj->link.connected depends on the state of other links in the
- * @cache. See also nmp_cache_link_connected_needs_toggle(). Given an ifindex
- * of a master, check if the cache contains such a master link that needs
- * toggling of the connected flag.
- *
- * Returns: NULL if there is no master link with ifindex @master_ifindex that should be toggled.
- *   Otherwise, return the link object from inside the cache with the given ifindex.
- *   The connected flag of that master should be toggled.
- */
-const NMPObject *
-nmp_cache_link_connected_needs_toggle_by_ifindex(const NMPCache * cache,
-                                                 int              master_ifindex,
-                                                 const NMPObject *potential_slave,
-                                                 const NMPObject *ignore_slave)
-{
-    const NMPObject *master;
-
-    if (master_ifindex > 0) {
-        master = nmp_cache_lookup_link(cache, master_ifindex);
-        if (nmp_cache_link_connected_needs_toggle(cache, master, potential_slave, ignore_slave))
-            return master;
-    }
-    return NULL;
-}
-
-/*****************************************************************************/
-
-static const NMDedupMultiEntry *
-_lookup_entry_with_idx_type(const NMPCache * cache,
-                            NMPCacheIdType   cache_id_type,
-                            const NMPObject *obj)
-{
-    const NMDedupMultiEntry *entry;
-
-    nm_assert(cache);
-    nm_assert(NMP_OBJECT_IS_VALID(obj));
-
-    entry =
-        nm_dedup_multi_index_lookup_obj(cache->multi_idx, _idx_type_get(cache, cache_id_type), obj);
-    nm_assert(!entry
-              || (NMP_OBJECT_IS_VALID(entry->obj)
-                  && NMP_OBJECT_GET_CLASS(entry->obj) == NMP_OBJECT_GET_CLASS(obj)));
-    return entry;
-}
-
-static const NMDedupMultiEntry *
-_lookup_entry(const NMPCache *cache, const NMPObject *obj)
-{
-    return _lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj);
-}
-
-const NMDedupMultiEntry *
-nmp_cache_lookup_entry_with_idx_type(const NMPCache * cache,
-                                     NMPCacheIdType   cache_id_type,
-                                     const NMPObject *obj)
-{
-    g_return_val_if_fail(cache, NULL);
-    g_return_val_if_fail(obj, NULL);
-    g_return_val_if_fail(cache_id_type > NMP_CACHE_ID_TYPE_NONE
-                             && cache_id_type <= NMP_CACHE_ID_TYPE_MAX,
-                         NULL);
-
-    return _lookup_entry_with_idx_type(cache, cache_id_type, obj);
-}
-
-const NMDedupMultiEntry *
-nmp_cache_lookup_entry(const NMPCache *cache, const NMPObject *obj)
-{
-    g_return_val_if_fail(cache, NULL);
-    g_return_val_if_fail(obj, NULL);
-
-    return _lookup_entry(cache, obj);
-}
-
-const NMDedupMultiEntry *
-nmp_cache_lookup_entry_link(const NMPCache *cache, int ifindex)
-{
-    NMPObject obj_needle;
-
-    g_return_val_if_fail(cache, NULL);
-    g_return_val_if_fail(ifindex > 0, NULL);
-
-    nmp_object_stackinit_id_link(&obj_needle, ifindex);
-    return _lookup_entry(cache, &obj_needle);
-}
-
-const NMPObject *
-nmp_cache_lookup_obj(const NMPCache *cache, const NMPObject *obj)
-{
-    return nm_dedup_multi_entry_get_obj(nmp_cache_lookup_entry(cache, obj));
-}
-
-const NMPObject *
-nmp_cache_lookup_link(const NMPCache *cache, int ifindex)
-{
-    return nm_dedup_multi_entry_get_obj(nmp_cache_lookup_entry_link(cache, ifindex));
-}
-
-/*****************************************************************************/
-
-const NMDedupMultiHeadEntry *
-nmp_cache_lookup_all(const NMPCache * cache,
-                     NMPCacheIdType   cache_id_type,
-                     const NMPObject *select_obj)
-{
-    nm_assert(cache);
-    nm_assert(NMP_OBJECT_IS_VALID(select_obj));
-
-    return nm_dedup_multi_index_lookup_head(cache->multi_idx,
-                                            _idx_type_get(cache, cache_id_type),
-                                            select_obj);
-}
-
-static const NMPLookup *
-_L(const NMPLookup *lookup)
-{
-#if NM_MORE_ASSERTS
-    DedupMultiIdxType idx_type;
-
-    nm_assert(lookup);
-    _dedup_multi_idx_type_init(&idx_type, lookup->cache_id_type);
-    nm_assert(
-        idx_type.parent.klass->idx_obj_partitionable((NMDedupMultiIdxType *) &idx_type,
-                                                     (NMDedupMultiObj *) &lookup->selector_obj));
-#endif
-    return lookup;
-}
-
-const NMPLookup *
-nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type)
-{
-    nm_assert(lookup);
-
-    switch (obj_type) {
-    case NMP_OBJECT_TYPE_LINK:
-    case NMP_OBJECT_TYPE_IP4_ADDRESS:
-    case NMP_OBJECT_TYPE_IP6_ADDRESS:
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-    case NMP_OBJECT_TYPE_ROUTING_RULE:
-    case NMP_OBJECT_TYPE_QDISC:
-    case NMP_OBJECT_TYPE_TFILTER:
-        _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
-        lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE;
-        return _L(lookup);
-    default:
-        nm_assert_not_reached();
-        return NULL;
-    }
-}
-
-const NMPLookup *
-nmp_lookup_init_link_by_ifname(NMPLookup *lookup, const char *ifname)
-{
-    NMPObject *o;
-
-    nm_assert(lookup);
-    nm_assert(ifname);
-    nm_assert(strlen(ifname) < IFNAMSIZ);
-
-    o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_LINK);
-    if (g_strlcpy(o->link.name, ifname, sizeof(o->link.name)) >= sizeof(o->link.name))
-        g_return_val_if_reached(NULL);
-    lookup->cache_id_type = NMP_CACHE_ID_TYPE_LINK_BY_IFNAME;
-    return _L(lookup);
-}
-
-const NMPLookup *
-nmp_lookup_init_object(NMPLookup *lookup, NMPObjectType obj_type, int ifindex)
-{
-    NMPObject *o;
-
-    nm_assert(lookup);
-    nm_assert(NM_IN_SET(obj_type,
-                        NMP_OBJECT_TYPE_IP4_ADDRESS,
-                        NMP_OBJECT_TYPE_IP6_ADDRESS,
-                        NMP_OBJECT_TYPE_IP4_ROUTE,
-                        NMP_OBJECT_TYPE_IP6_ROUTE,
-                        NMP_OBJECT_TYPE_QDISC,
-                        NMP_OBJECT_TYPE_TFILTER));
-
-    if (ifindex <= 0) {
-        return nmp_lookup_init_obj_type(lookup, obj_type);
-    }
-
-    o                           = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
-    o->obj_with_ifindex.ifindex = ifindex;
-    lookup->cache_id_type       = NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX;
-    return _L(lookup);
-}
-
-const NMPLookup *
-nmp_lookup_init_route_default(NMPLookup *lookup, NMPObjectType obj_type)
-{
-    NMPObject *o;
-
-    nm_assert(lookup);
-    nm_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
-
-    o                     = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
-    o->ip_route.ifindex   = 1;
-    lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES;
-    return _L(lookup);
-}
-
-const NMPLookup *
-nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj)
-{
-    const NMPlatformIP4Route *r4;
-    const NMPlatformIP6Route *r6;
-
-    nm_assert(lookup);
-
-    switch (NMP_OBJECT_GET_TYPE(obj)) {
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-        r4 = NMP_OBJECT_CAST_IP4_ROUTE(obj);
-        return nmp_lookup_init_ip4_route_by_weak_id(lookup,
-                                                    r4->network,
-                                                    r4->plen,
-                                                    r4->metric,
-                                                    r4->tos);
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-        r6 = NMP_OBJECT_CAST_IP6_ROUTE(obj);
-        return nmp_lookup_init_ip6_route_by_weak_id(lookup,
-                                                    &r6->network,
-                                                    r6->plen,
-                                                    r6->metric,
-                                                    &r6->src,
-                                                    r6->src_plen);
-    default:
-        nm_assert_not_reached();
-        return NULL;
-    }
-}
-
-const NMPLookup *
-nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup,
-                                     in_addr_t  network,
-                                     guint      plen,
-                                     guint32    metric,
-                                     guint8     tos)
-{
-    NMPObject *o;
-
-    nm_assert(lookup);
-
-    o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE);
-    o->ip4_route.ifindex = 1;
-    o->ip4_route.plen    = plen;
-    o->ip4_route.metric  = metric;
-    if (network)
-        o->ip4_route.network = network;
-    o->ip4_route.tos      = tos;
-    lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID;
-    return _L(lookup);
-}
-
-const NMPLookup *
-nmp_lookup_init_ip6_route_by_weak_id(NMPLookup *            lookup,
-                                     const struct in6_addr *network,
-                                     guint                  plen,
-                                     guint32                metric,
-                                     const struct in6_addr *src,
-                                     guint8                 src_plen)
-{
-    NMPObject *o;
-
-    nm_assert(lookup);
-
-    o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE);
-    o->ip6_route.ifindex = 1;
-    o->ip6_route.plen    = plen;
-    o->ip6_route.metric  = metric;
-    if (network)
-        o->ip6_route.network = *network;
-    if (src)
-        o->ip6_route.src = *src;
-    o->ip6_route.src_plen = src_plen;
-    lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID;
-    return _L(lookup);
-}
-
-const NMPLookup *
-nmp_lookup_init_object_by_addr_family(NMPLookup *lookup, NMPObjectType obj_type, int addr_family)
-{
-    NMPObject *o;
-
-    nm_assert(lookup);
-    nm_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_ROUTING_RULE));
-
-    if (addr_family == AF_UNSPEC)
-        return nmp_lookup_init_obj_type(lookup, obj_type);
-
-    nm_assert_addr_family(addr_family);
-    o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
-    NMP_OBJECT_CAST_ROUTING_RULE(o)->addr_family = addr_family;
-    lookup->cache_id_type                        = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY;
-    return _L(lookup);
-}
-
-/*****************************************************************************/
-
-GArray *
-nmp_cache_lookup_to_array(const NMDedupMultiHeadEntry *head_entry,
-                          NMPObjectType                obj_type,
-                          gboolean                     visible_only)
-{
-    const NMPClass * klass = nmp_class_from_type(obj_type);
-    NMDedupMultiIter iter;
-    const NMPObject *o;
-    GArray *         array;
-
-    g_return_val_if_fail(klass, NULL);
-
-    array = g_array_sized_new(FALSE, FALSE, klass->sizeof_public, head_entry ? head_entry->len : 0);
-    nmp_cache_iter_for_each (&iter, head_entry, &o) {
-        nm_assert(NMP_OBJECT_GET_CLASS(o) == klass);
-        if (visible_only && !nmp_object_is_visible(o))
-            continue;
-        g_array_append_vals(array, &o->object, 1);
-    }
-    return array;
-}
-
-/*****************************************************************************/
-
-const NMPObject *
-nmp_cache_lookup_link_full(const NMPCache * cache,
-                           int              ifindex,
-                           const char *     ifname,
-                           gboolean         visible_only,
-                           NMLinkType       link_type,
-                           NMPObjectMatchFn match_fn,
-                           gpointer         user_data)
-{
-    NMPObject                    obj_needle;
-    const NMPObject *            obj;
-    NMDedupMultiIter             iter;
-    const NMDedupMultiHeadEntry *head_entry;
-    const NMPlatformLink *       link = NULL;
-    NMPLookup                    lookup;
-
-    if (ifindex > 0) {
-        obj = nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&obj_needle, ifindex));
-
-        if (!obj || (visible_only && !nmp_object_is_visible(obj))
-            || (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type)
-            || (ifname && strcmp(obj->link.name, ifname))
-            || (match_fn && !match_fn(obj, user_data)))
-            return NULL;
-        return obj;
-    } else if (!ifname && !match_fn)
-        return NULL;
-    else {
-        const NMPObject *obj_best = NULL;
-
-        if (ifname) {
-            if (strlen(ifname) >= IFNAMSIZ)
-                return NULL;
-            nmp_lookup_init_link_by_ifname(&lookup, ifname);
-        } else
-            nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK);
-
-        head_entry = nmp_cache_lookup(cache, &lookup);
-        nmp_cache_iter_for_each_link (&iter, head_entry, &link) {
-            obj = NMP_OBJECT_UP_CAST(link);
-
-            if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type)
-                continue;
-            if (visible_only && !nmp_object_is_visible(obj))
-                continue;
-            if (match_fn && !match_fn(obj, user_data))
-                continue;
-
-            /* if there are multiple candidates, prefer the visible ones. */
-            if (visible_only || nmp_object_is_visible(obj))
-                return obj;
-            if (!obj_best)
-                obj_best = obj;
-        }
-        return obj_best;
-    }
-}
-
-/*****************************************************************************/
-
-static NMDedupMultiIdxMode
-_obj_get_add_mode(const NMPObject *obj)
-{
-    /* new objects are usually appended to the list. Except for
-     * addresses, which are prepended during `ip address add`.
-     *
-     * Actually, for routes it is more complicated, because depending on
-     * `ip route append`, `ip route replace`, `ip route prepend`, the object
-     * will be added at the tail, at the front, or even replace an element
-     * in the list. However, that is handled separately by nmp_cache_update_netlink_route()
-     * and of no concern here. */
-    if (NM_IN_SET(NMP_OBJECT_GET_TYPE(obj),
-                  NMP_OBJECT_TYPE_IP4_ADDRESS,
-                  NMP_OBJECT_TYPE_IP6_ADDRESS))
-        return NM_DEDUP_MULTI_IDX_MODE_PREPEND;
-    return NM_DEDUP_MULTI_IDX_MODE_APPEND;
-}
-
-static void
-_idxcache_update_order_for_dump(NMPCache *cache, const NMDedupMultiEntry *entry)
-{
-    const NMPClass *         klass;
-    const guint8 *           i_idx_type;
-    const NMDedupMultiEntry *entry2;
-
-    nm_dedup_multi_entry_reorder(entry, NULL, TRUE);
-
-    klass = NMP_OBJECT_GET_CLASS(entry->obj);
-    for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) {
-        NMPCacheIdType id_type = *i_idx_type;
-
-        if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE)
-            continue;
-
-        entry2 = nm_dedup_multi_index_lookup_obj(cache->multi_idx,
-                                                 _idx_type_get(cache, id_type),
-                                                 entry->obj);
-        if (!entry2)
-            continue;
-
-        nm_assert(entry2 != entry);
-        nm_assert(entry2->obj == entry->obj);
-
-        nm_dedup_multi_entry_reorder(entry2, NULL, TRUE);
-    }
-}
-
-static void
-_idxcache_update_other_cache_ids(NMPCache *       cache,
-                                 NMPCacheIdType   cache_id_type,
-                                 const NMPObject *obj_old,
-                                 const NMPObject *obj_new,
-                                 gboolean         is_dump)
-{
-    const NMDedupMultiEntry *entry_new;
-    const NMDedupMultiEntry *entry_old;
-    const NMDedupMultiEntry *entry_order;
-    NMDedupMultiIdxType *    idx_type;
-
-    nm_assert(obj_new || obj_old);
-    nm_assert(!obj_new || NMP_OBJECT_GET_TYPE(obj_new) != NMP_OBJECT_TYPE_UNKNOWN);
-    nm_assert(!obj_old || NMP_OBJECT_GET_TYPE(obj_old) != NMP_OBJECT_TYPE_UNKNOWN);
-    nm_assert(!obj_old || !obj_new
-              || NMP_OBJECT_GET_CLASS(obj_new) == NMP_OBJECT_GET_CLASS(obj_old));
-    nm_assert(!obj_old || !obj_new || !nmp_object_equal(obj_new, obj_old));
-    nm_assert(!obj_new || obj_new == nm_dedup_multi_index_obj_find(cache->multi_idx, obj_new));
-    nm_assert(!obj_old || obj_old == nm_dedup_multi_index_obj_find(cache->multi_idx, obj_old));
-
-    idx_type = _idx_type_get(cache, cache_id_type);
-
-    if (obj_old) {
-        entry_old = nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type, obj_old);
-        if (!obj_new) {
-            if (entry_old)
-                nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old);
-            return;
-        }
-    } else
-        entry_old = NULL;
-
-    if (obj_new) {
-        if (obj_old && nm_dedup_multi_idx_type_id_equal(idx_type, obj_old, obj_new)
-            && nm_dedup_multi_idx_type_partition_equal(idx_type, obj_old, obj_new)) {
-            /* optimize. We just looked up the @obj_old entry and @obj_new compares equal
-             * according to idx_obj_id_equal(). entry_new is the same as entry_old. */
-            entry_new = entry_old;
-        } else {
-            entry_new = nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type, obj_new);
-        }
-
-        if (entry_new)
-            entry_order = entry_new;
-        else if (entry_old
-                 && nm_dedup_multi_idx_type_partition_equal(idx_type, entry_old->obj, obj_new))
-            entry_order = entry_old;
-        else
-            entry_order = NULL;
-        nm_dedup_multi_index_add_full(
-            cache->multi_idx,
-            idx_type,
-            obj_new,
-            is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE : _obj_get_add_mode(obj_new),
-            is_dump ? NULL : entry_order,
-            entry_new ?: NM_DEDUP_MULTI_ENTRY_MISSING,
-            entry_new ? entry_new->head : (entry_order ? entry_order->head : NULL),
-            &entry_new,
-            NULL);
-
-#if NM_MORE_ASSERTS
-        if (entry_new) {
-            nm_assert(idx_type->klass->idx_obj_partitionable);
-            nm_assert(idx_type->klass->idx_obj_partition_equal);
-            nm_assert(idx_type->klass->idx_obj_partitionable(idx_type, entry_new->obj));
-            nm_assert(idx_type->klass->idx_obj_partition_equal(idx_type,
-                                                               (gpointer) obj_new,
-                                                               entry_new->obj));
-        }
-#endif
-    } else
-        entry_new = NULL;
-
-    if (entry_old && entry_old != entry_new)
-        nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old);
-}
-
-static void
-_idxcache_update(NMPCache *                cache,
-                 const NMDedupMultiEntry * entry_old,
-                 NMPObject *               obj_new,
-                 gboolean                  is_dump,
-                 const NMDedupMultiEntry **out_entry_new)
-{
-    const NMPClass *         klass;
-    const guint8 *           i_idx_type;
-    NMDedupMultiIdxType *    idx_type_o     = _idx_type_get(cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE);
-    const NMDedupMultiEntry *entry_new      = NULL;
-    nm_auto_nmpobj const NMPObject *obj_old = NULL;
-
-    /* we update an object in the cache.
-     *
-     * Note that @entry_old MUST be what is currently tracked in multi_idx, and it must
-     * have the same ID as @obj_new. */
-
-    nm_assert(cache);
-    nm_assert(entry_old || obj_new);
-    nm_assert(!obj_new || nmp_object_is_alive(obj_new));
-    nm_assert(
-        !entry_old
-        || entry_old
-               == nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type_o, entry_old->obj));
-    nm_assert(!obj_new
-              || entry_old
-                     == nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type_o, obj_new));
-    nm_assert(!entry_old || entry_old->head->idx_type == idx_type_o);
-    nm_assert(!entry_old || !obj_new
-              || nm_dedup_multi_idx_type_partition_equal(idx_type_o, entry_old->obj, obj_new));
-    nm_assert(!entry_old || !obj_new
-              || nm_dedup_multi_idx_type_id_equal(idx_type_o, entry_old->obj, obj_new));
-    nm_assert(!entry_old || !obj_new
-              || (obj_new->parent.klass == ((const NMPObject *) entry_old->obj)->parent.klass
-                  && !obj_new->parent.klass->obj_full_equal((NMDedupMultiObj *) obj_new,
-                                                            entry_old->obj)));
-
-    /* keep a reference to the pre-existing entry */
-    if (entry_old)
-        obj_old = nmp_object_ref(entry_old->obj);
-
-    /* first update the main index NMP_CACHE_ID_TYPE_OBJECT_TYPE.
-     * We already know the pre-existing @entry old, so all that
-     * nm_dedup_multi_index_add_full() effectively does, is update the
-     * obj reference.
-     *
-     * We also get the new boxed object, which we need below. */
-    if (obj_new) {
-        nm_auto_nmpobj NMPObject *obj_old2 = NULL;
-
-        nm_dedup_multi_index_add_full(cache->multi_idx,
-                                      idx_type_o,
-                                      obj_new,
-                                      is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
-                                              : _obj_get_add_mode(obj_new),
-                                      NULL,
-                                      entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING,
-                                      NULL,
-                                      &entry_new,
-                                      (const NMDedupMultiObj **) &obj_old2);
-        nm_assert(entry_new);
-        nm_assert(obj_old == obj_old2);
-        nm_assert(!entry_old || entry_old == entry_new);
-    } else
-        nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old);
-
-    /* now update all other indexes. We know the previously boxed entry, and the
-     * newly boxed one. */
-    klass = NMP_OBJECT_GET_CLASS(entry_new ? entry_new->obj : obj_old);
-    for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) {
-        NMPCacheIdType id_type = *i_idx_type;
-
-        if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE)
-            continue;
-        _idxcache_update_other_cache_ids(cache,
-                                         id_type,
-                                         obj_old,
-                                         entry_new ? entry_new->obj : NULL,
-                                         is_dump);
-    }
-
-    NM_SET_OUT(out_entry_new, entry_new);
-}
-
-NMPCacheOpsType
-nmp_cache_remove(NMPCache *        cache,
-                 const NMPObject * obj_needle,
-                 gboolean          equals_by_ptr,
-                 gboolean          only_dirty,
-                 const NMPObject **out_obj_old)
-{
-    const NMDedupMultiEntry *entry_old;
-    const NMPObject *        obj_old;
-
-    entry_old = _lookup_entry(cache, obj_needle);
-
-    if (!entry_old) {
-        NM_SET_OUT(out_obj_old, NULL);
-        return NMP_CACHE_OPS_UNCHANGED;
-    }
-
-    obj_old = entry_old->obj;
-
-    NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-
-    if (equals_by_ptr && obj_old != obj_needle) {
-        /* We found an identical object, but we only delete it if it's the same pointer as
-         * @obj_needle. */
-        return NMP_CACHE_OPS_UNCHANGED;
-    }
-    if (only_dirty && !entry_old->dirty) {
-        /* the entry is not dirty. Skip. */
-        return NMP_CACHE_OPS_UNCHANGED;
-    }
-    _idxcache_update(cache, entry_old, NULL, FALSE, NULL);
-    return NMP_CACHE_OPS_REMOVED;
-}
-
-NMPCacheOpsType
-nmp_cache_remove_netlink(NMPCache *        cache,
-                         const NMPObject * obj_needle,
-                         const NMPObject **out_obj_old,
-                         const NMPObject **out_obj_new)
-{
-    const NMDedupMultiEntry *entry_old;
-    const NMDedupMultiEntry *entry_new = NULL;
-    const NMPObject *        obj_old;
-    nm_auto_nmpobj NMPObject *obj_new = NULL;
-
-    entry_old = _lookup_entry(cache, obj_needle);
-
-    if (!entry_old) {
-        NM_SET_OUT(out_obj_old, NULL);
-        NM_SET_OUT(out_obj_new, NULL);
-        return NMP_CACHE_OPS_UNCHANGED;
-    }
-
-    obj_old = entry_old->obj;
-
-    if (NMP_OBJECT_GET_TYPE(obj_needle) == NMP_OBJECT_TYPE_LINK) {
-        /* For nmp_cache_remove_netlink() we have an incomplete @obj_needle instance to be
-         * removed from netlink. Link objects are alive without being in netlink when they
-         * have a udev-device. All we want to do in this case is clear the netlink.is_in_netlink
-         * flag. */
-
-        NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-
-        if (!obj_old->_link.netlink.is_in_netlink) {
-            nm_assert(obj_old->_link.udev.device);
-            NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
-            return NMP_CACHE_OPS_UNCHANGED;
-        }
-
-        if (!obj_old->_link.udev.device) {
-            /* the update would make @obj_old invalid. Remove it. */
-            _idxcache_update(cache, entry_old, NULL, FALSE, NULL);
-            NM_SET_OUT(out_obj_new, NULL);
-            return NMP_CACHE_OPS_REMOVED;
-        }
-
-        obj_new                              = nmp_object_clone(obj_old, FALSE);
-        obj_new->_link.netlink.is_in_netlink = FALSE;
-
-        _nmp_object_fixup_link_master_connected(&obj_new, NULL, cache);
-        _nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev);
-
-        _idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new);
-        NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
-        return NMP_CACHE_OPS_UPDATED;
-    }
-
-    NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-    NM_SET_OUT(out_obj_new, NULL);
-    _idxcache_update(cache, entry_old, NULL, FALSE, NULL);
-    return NMP_CACHE_OPS_REMOVED;
-}
-
-/**
- * nmp_cache_update_netlink:
- * @cache: the platform cache
- * @obj_hand_over: a #NMPObject instance as received from netlink and created via
- *    nmp_object_from_nl(). Especially for link, it must not have the udev
- *    replated fields set.
- *    This instance will be modified and might be put into the cache. When
- *    calling nmp_cache_update_netlink() you hand @obj over to the cache.
- *    Except, that the cache will increment the ref count as appropriate. You
- *    must still unref the obj to release your part of the ownership.
- * @is_dump: whether this update comes during a dump of object of the same kind.
- *    kernel dumps objects in a certain order, which matters especially for routes.
- *    Before a dump we mark all objects as dirty, and remove all untouched objects
- *    afterwards. Hence, during a dump, every update should move the object to the
- *    end of the list, to obtain the correct order. That means, to use NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE,
- *    instead of NM_DEDUP_MULTI_IDX_MODE_APPEND.
- * @out_obj_old: (allow-none) (out): return the object with same ID as @obj_hand_over,
- *    that was in the cache before update. If an object is returned, the caller must
- *    unref it afterwards.
- * @out_obj_new: (allow-none) (out): return the object from the cache after update.
- *    The caller must unref this object.
- *
- * Returns: how the cache changed.
- *
- * Even if there was no change in the cache (NMP_CACHE_OPS_UNCHANGED), @out_obj_old
- * and @out_obj_new will be set accordingly.
- **/
-NMPCacheOpsType
-nmp_cache_update_netlink(NMPCache *        cache,
-                         NMPObject *       obj_hand_over,
-                         gboolean          is_dump,
-                         const NMPObject **out_obj_old,
-                         const NMPObject **out_obj_new)
-{
-    const NMDedupMultiEntry *entry_old;
-    const NMDedupMultiEntry *entry_new;
-    const NMPObject *        obj_old;
-    gboolean                 is_alive;
-
-    nm_assert(cache);
-    nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over));
-    nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over));
-    /* A link object from netlink must have the udev related fields unset.
-     * We could implement to handle that, but there is no need to support such
-     * a use-case */
-    nm_assert(NMP_OBJECT_GET_TYPE(obj_hand_over) != NMP_OBJECT_TYPE_LINK
-              || (!obj_hand_over->_link.udev.device && !obj_hand_over->link.driver));
-    nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over);
-
-    entry_old = _lookup_entry(cache, obj_hand_over);
-
-    if (!entry_old) {
-        NM_SET_OUT(out_obj_old, NULL);
-
-        if (!nmp_object_is_alive(obj_hand_over)) {
-            NM_SET_OUT(out_obj_new, NULL);
-            return NMP_CACHE_OPS_UNCHANGED;
-        }
-
-        if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_LINK) {
-            _nmp_object_fixup_link_master_connected(&obj_hand_over, NULL, cache);
-            _nmp_object_fixup_link_udev_fields(&obj_hand_over, NULL, cache->use_udev);
-        }
-
-        _idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new);
-        NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
-        return NMP_CACHE_OPS_ADDED;
-    }
-
-    obj_old = entry_old->obj;
-
-    if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_LINK) {
-        if (!obj_hand_over->_link.netlink.is_in_netlink) {
-            if (!obj_old->_link.netlink.is_in_netlink) {
-                nm_assert(obj_old->_link.udev.device);
-                NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-                NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
-                return NMP_CACHE_OPS_UNCHANGED;
-            }
-            if (obj_old->_link.udev.device) {
-                /* @obj_hand_over is not in netlink.
-                 *
-                 * This is similar to nmp_cache_remove_netlink(), but there we preserve the
-                 * preexisting netlink properties. The use case of that is when kernel_get_object()
-                 * cannot load an object (based on the id of a needle).
-                 *
-                 * Here we keep the data provided from @obj_hand_over. The usecase is when receiving
-                 * a valid @obj_hand_over instance from netlink with RTM_DELROUTE.
-                 */
-                is_alive = TRUE;
-            } else
-                is_alive = FALSE;
-        } else
-            is_alive = TRUE;
-
-        if (is_alive) {
-            _nmp_object_fixup_link_master_connected(&obj_hand_over, NULL, cache);
-
-            /* Merge the netlink parts with what we have from udev. */
-            udev_device_unref(obj_hand_over->_link.udev.device);
-            obj_hand_over->_link.udev.device =
-                obj_old->_link.udev.device ? udev_device_ref(obj_old->_link.udev.device) : NULL;
-            _nmp_object_fixup_link_udev_fields(&obj_hand_over, NULL, cache->use_udev);
-
-            if (obj_hand_over->_link.netlink.lnk) {
-                nm_auto_nmpobj const NMPObject *lnk_old = obj_hand_over->_link.netlink.lnk;
-
-                /* let's dedup/intern the lnk object. */
-                obj_hand_over->_link.netlink.lnk =
-                    nm_dedup_multi_index_obj_intern(cache->multi_idx, lnk_old);
-            }
-        }
-    } else
-        is_alive = nmp_object_is_alive(obj_hand_over);
-
-    NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-
-    if (!is_alive) {
-        /* the update would make @obj_old invalid. Remove it. */
-        _idxcache_update(cache, entry_old, NULL, FALSE, NULL);
-        NM_SET_OUT(out_obj_new, NULL);
-        return NMP_CACHE_OPS_REMOVED;
-    }
-
-    if (nmp_object_equal(obj_old, obj_hand_over)) {
-        if (is_dump)
-            _idxcache_update_order_for_dump(cache, entry_old);
-        nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
-        NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
-        return NMP_CACHE_OPS_UNCHANGED;
-    }
-
-    _idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new);
-    NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
-    return NMP_CACHE_OPS_UPDATED;
-}
-
-NMPCacheOpsType
-nmp_cache_update_netlink_route(NMPCache *        cache,
-                               NMPObject *       obj_hand_over,
-                               gboolean          is_dump,
-                               guint16           nlmsgflags,
-                               const NMPObject **out_obj_old,
-                               const NMPObject **out_obj_new,
-                               const NMPObject **out_obj_replace,
-                               gboolean *        out_resync_required)
-{
-    NMDedupMultiIter             iter;
-    const NMDedupMultiEntry *    entry_old;
-    const NMDedupMultiEntry *    entry_new;
-    const NMDedupMultiEntry *    entry_cur;
-    const NMDedupMultiEntry *    entry_replace;
-    const NMDedupMultiHeadEntry *head_entry;
-    gboolean                     is_alive;
-    NMPCacheOpsType              ops_type = NMP_CACHE_OPS_UNCHANGED;
-    gboolean                     resync_required;
-
-    nm_assert(cache);
-    nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over));
-    nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over));
-    /* A link object from netlink must have the udev related fields unset.
-     * We could implement to handle that, but there is no need to support such
-     * a use-case */
-    nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_hand_over),
-                        NMP_OBJECT_TYPE_IP4_ROUTE,
-                        NMP_OBJECT_TYPE_IP6_ROUTE));
-    nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over);
-
-    entry_old = _lookup_entry(cache, obj_hand_over);
-    entry_new = NULL;
-
-    NM_SET_OUT(out_obj_old, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_old)));
-
-    if (!entry_old) {
-        if (!nmp_object_is_alive(obj_hand_over))
-            goto update_done;
-
-        _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new);
-        ops_type = NMP_CACHE_OPS_ADDED;
-        goto update_done;
-    }
-
-    is_alive = nmp_object_is_alive(obj_hand_over);
-
-    if (!is_alive) {
-        /* the update would make @entry_old invalid. Remove it. */
-        _idxcache_update(cache, entry_old, NULL, FALSE, NULL);
-        ops_type = NMP_CACHE_OPS_REMOVED;
-        goto update_done;
-    }
-
-    if (nmp_object_equal(entry_old->obj, obj_hand_over)) {
-        if (is_dump)
-            _idxcache_update_order_for_dump(cache, entry_old);
-        nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
-        goto update_done;
-    }
-
-    _idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new);
-    ops_type = NMP_CACHE_OPS_UPDATED;
-
-update_done:
-    NM_SET_OUT(out_obj_new, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_new)));
-
-    /* a RTM_GETROUTE event may signal that another object was replaced.
-     * Find out whether that is the case and return it as @obj_replaced.
-     *
-     * Also, fixup the order of @entry_new within NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID
-     * index. For most parts, we don't care about the order of objects (including routes).
-     * But NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID we must keep in the correct order, to
-     * properly find @obj_replaced. */
-    resync_required = FALSE;
-    entry_replace   = NULL;
-    if (is_dump)
-        goto out;
-
-    if (!entry_new) {
-        if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE)
-            && nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
-            /* hm. @obj_hand_over was not added, meaning it was not alive.
-             * However, we track some other objects with the same weak-id.
-             * It's unclear what that means. To be sure, resync. */
-            resync_required = TRUE;
-        }
-        goto out;
-    }
-
-    /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID
-     * index. For all other indexes their order becomes messed up. */
-    entry_cur =
-        _lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj);
-    if (!entry_cur) {
-        nm_assert_not_reached();
-        goto out;
-    }
-    nm_assert(entry_cur->obj == entry_new->obj);
-
-    head_entry = entry_cur->head;
-    nm_assert(head_entry
-              == nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_cur->obj));
-
-    if (head_entry->len == 1) {
-        /* there is only one object, and we expect it to be @obj_new. */
-        nm_assert(nm_dedup_multi_head_entry_get_idx(head_entry, 0) == entry_cur);
-        goto out;
-    }
-
-    switch (nlmsgflags & (NLM_F_REPLACE | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND)) {
-    case NLM_F_REPLACE:
-        /* ip route change */
-
-        /* get the first element (but skip @obj_new). */
-        nm_dedup_multi_iter_init(&iter, head_entry);
-        if (!nm_dedup_multi_iter_next(&iter))
-            nm_assert_not_reached();
-        if (iter.current == entry_cur) {
-            if (!nm_dedup_multi_iter_next(&iter))
-                nm_assert_not_reached();
-        }
-        entry_replace = iter.current;
-
-        nm_assert(entry_replace && entry_cur != entry_replace);
-
-        nm_dedup_multi_entry_reorder(entry_cur, entry_replace, FALSE);
-        break;
-    case NLM_F_CREATE | NLM_F_APPEND:
-        /* ip route append */
-        nm_dedup_multi_entry_reorder(entry_cur, NULL, TRUE);
-        break;
-    case NLM_F_CREATE:
-        /* ip route prepend */
-        nm_dedup_multi_entry_reorder(entry_cur, NULL, FALSE);
-        break;
-    default:
-        /* this is an unexpected case, probably a bug that we need to handle better. */
-        resync_required = TRUE;
-        break;
-    }
-
-out:
-    NM_SET_OUT(out_obj_replace, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_replace)));
-    NM_SET_OUT(out_resync_required, resync_required);
-    return ops_type;
-}
-
-NMPCacheOpsType
-nmp_cache_update_link_udev(NMPCache *          cache,
-                           int                 ifindex,
-                           struct udev_device *udevice,
-                           const NMPObject **  out_obj_old,
-                           const NMPObject **  out_obj_new)
-{
-    const NMPObject *obj_old;
-    nm_auto_nmpobj NMPObject *obj_new = NULL;
-    const NMDedupMultiEntry * entry_old;
-    const NMDedupMultiEntry * entry_new;
-
-    entry_old = nmp_cache_lookup_entry_link(cache, ifindex);
-
-    if (!entry_old) {
-        if (!udevice) {
-            NM_SET_OUT(out_obj_old, NULL);
-            NM_SET_OUT(out_obj_new, NULL);
-            return NMP_CACHE_OPS_UNCHANGED;
-        }
-
-        obj_new                    = nmp_object_new(NMP_OBJECT_TYPE_LINK, NULL);
-        obj_new->link.ifindex      = ifindex;
-        obj_new->_link.udev.device = udev_device_ref(udevice);
-
-        _nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev);
-
-        _idxcache_update(cache, NULL, obj_new, FALSE, &entry_new);
-        NM_SET_OUT(out_obj_old, NULL);
-        NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
-        return NMP_CACHE_OPS_ADDED;
-    } else {
-        obj_old = entry_old->obj;
-        NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-
-        if (obj_old->_link.udev.device == udevice) {
-            NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
-            return NMP_CACHE_OPS_UNCHANGED;
-        }
-
-        if (!udevice && !obj_old->_link.netlink.is_in_netlink) {
-            /* the update would make @obj_old invalid. Remove it. */
-            _idxcache_update(cache, entry_old, NULL, FALSE, NULL);
-            NM_SET_OUT(out_obj_new, NULL);
-            return NMP_CACHE_OPS_REMOVED;
-        }
-
-        obj_new = nmp_object_clone(obj_old, FALSE);
-
-        udev_device_unref(obj_new->_link.udev.device);
-        obj_new->_link.udev.device = udevice ? udev_device_ref(udevice) : NULL;
-
-        _nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev);
-
-        _idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new);
-        NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
-        return NMP_CACHE_OPS_UPDATED;
-    }
-}
-
-NMPCacheOpsType
-nmp_cache_update_link_master_connected(NMPCache *        cache,
-                                       int               ifindex,
-                                       const NMPObject **out_obj_old,
-                                       const NMPObject **out_obj_new)
-{
-    const NMDedupMultiEntry *entry_old;
-    const NMDedupMultiEntry *entry_new = NULL;
-    const NMPObject *        obj_old;
-    nm_auto_nmpobj NMPObject *obj_new = NULL;
-
-    entry_old = nmp_cache_lookup_entry_link(cache, ifindex);
-
-    if (!entry_old) {
-        NM_SET_OUT(out_obj_old, NULL);
-        NM_SET_OUT(out_obj_new, NULL);
-        return NMP_CACHE_OPS_UNCHANGED;
-    }
-
-    obj_old = entry_old->obj;
-
-    if (!nmp_cache_link_connected_needs_toggle(cache, obj_old, NULL, NULL)) {
-        NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-        NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
-        return NMP_CACHE_OPS_UNCHANGED;
-    }
-
-    obj_new                 = nmp_object_clone(obj_old, FALSE);
-    obj_new->link.connected = !obj_old->link.connected;
-
-    NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
-    _idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new);
-    NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
-    return NMP_CACHE_OPS_UPDATED;
-}
-
-/*****************************************************************************/
-
-void
-nmp_cache_dirty_set_all_main(NMPCache *cache, const NMPLookup *lookup)
-{
-    const NMDedupMultiHeadEntry *head_entry;
-    NMDedupMultiIter             iter;
-
-    nm_assert(cache);
-    nm_assert(lookup);
-
-    head_entry = nmp_cache_lookup(cache, lookup);
-
-    nm_dedup_multi_iter_init(&iter, head_entry);
-    while (nm_dedup_multi_iter_next(&iter)) {
-        const NMDedupMultiEntry *main_entry;
-
-        main_entry = nmp_cache_reresolve_main_entry(cache, iter.current, lookup);
-
-        nm_dedup_multi_entry_set_dirty(main_entry, TRUE);
-    }
-}
-
-/*****************************************************************************/
-
-NMPCache *
-nmp_cache_new(NMDedupMultiIndex *multi_idx, gboolean use_udev)
-{
-    NMPCache *cache = g_slice_new0(NMPCache);
-    guint     i;
-
-    for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++)
-        _dedup_multi_idx_type_init((DedupMultiIdxType *) _idx_type_get(cache, i), i);
-
-    cache->multi_idx = nm_dedup_multi_index_ref(multi_idx);
-
-    cache->use_udev = !!use_udev;
-    return cache;
-}
-
-void
-nmp_cache_free(NMPCache *cache)
-{
-    guint i;
-
-    for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++)
-        nm_dedup_multi_index_remove_idx(cache->multi_idx, _idx_type_get(cache, i));
-
-    nm_dedup_multi_index_unref(cache->multi_idx);
-
-    g_slice_free(NMPCache, cache);
-}
-
-/*****************************************************************************/
-
-void
-nmtst_assert_nmp_cache_is_consistent(const NMPCache *cache)
-{}
-
-/*****************************************************************************/
-
-/* below, ensure that addr_family get's automatically initialize to AF_UNSPEC. */
-G_STATIC_ASSERT(AF_UNSPEC == 0);
-
-typedef const char *(*CmdPlobjToStringFunc)(const NMPlatformObject *obj, char *buf, gsize len);
-typedef const char *(*CmdPlobjToStringIdFunc)(const NMPlatformObject *obj, char *buf, gsize len);
-typedef void (*CmdPlobjHashUpdateFunc)(const NMPlatformObject *obj, NMHashState *h);
-typedef int (*CmdPlobjCmpFunc)(const NMPlatformObject *obj1, const NMPlatformObject *obj2);
-
-const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
-    [NMP_OBJECT_TYPE_LINK - 1] =
-        {
-            .parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type                 = NMP_OBJECT_TYPE_LINK,
-            .sizeof_data              = sizeof(NMPObjectLink),
-            .sizeof_public            = sizeof(NMPlatformLink),
-            .obj_type_name            = "link",
-            .rtm_gettype              = RTM_GETLINK,
-            .signal_type_id           = NM_PLATFORM_SIGNAL_ID_LINK,
-            .signal_type              = NM_PLATFORM_SIGNAL_LINK_CHANGED,
-            .supported_cache_ids      = _supported_cache_ids_link,
-            .cmd_obj_hash_update      = _vt_cmd_obj_hash_update_link,
-            .cmd_obj_cmp              = _vt_cmd_obj_cmp_link,
-            .cmd_obj_copy             = _vt_cmd_obj_copy_link,
-            .cmd_obj_dispose          = _vt_cmd_obj_dispose_link,
-            .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_link,
-            .cmd_obj_is_visible       = _vt_cmd_obj_is_visible_link,
-            .cmd_obj_to_string        = _vt_cmd_obj_to_string_link,
-            .cmd_plobj_id_copy        = _vt_cmd_plobj_id_copy_link,
-            .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_link,
-            .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_link,
-            .cmd_plobj_to_string_id   = _vt_cmd_plobj_to_string_id_link,
-            .cmd_plobj_to_string      = (CmdPlobjToStringFunc) nm_platform_link_to_string,
-            .cmd_plobj_hash_update    = (CmdPlobjHashUpdateFunc) nm_platform_link_hash_update,
-            .cmd_plobj_cmp            = (CmdPlobjCmpFunc) nm_platform_link_cmp,
-        },
-    [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] =
-        {
-            .parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type                 = NMP_OBJECT_TYPE_IP4_ADDRESS,
-            .sizeof_data              = sizeof(NMPObjectIP4Address),
-            .sizeof_public            = sizeof(NMPlatformIP4Address),
-            .obj_type_name            = "ip4-address",
-            .addr_family              = AF_INET,
-            .rtm_gettype              = RTM_GETADDR,
-            .signal_type_id           = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS,
-            .signal_type              = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED,
-            .supported_cache_ids      = _supported_cache_ids_ipx_address,
-            .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_ipx_address,
-            .cmd_plobj_id_copy        = _vt_cmd_plobj_id_copy_ip4_address,
-            .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_ip4_address,
-            .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_address,
-            .cmd_plobj_to_string_id   = _vt_cmd_plobj_to_string_id_ip4_address,
-            .cmd_plobj_to_string      = (CmdPlobjToStringFunc) nm_platform_ip4_address_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip4_address_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp,
-        },
-    [NMP_OBJECT_TYPE_IP6_ADDRESS
-        - 1] = {.parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-                .obj_type                 = NMP_OBJECT_TYPE_IP6_ADDRESS,
-                .sizeof_data              = sizeof(NMPObjectIP6Address),
-                .sizeof_public            = sizeof(NMPlatformIP6Address),
-                .obj_type_name            = "ip6-address",
-                .addr_family              = AF_INET6,
-                .rtm_gettype              = RTM_GETADDR,
-                .signal_type_id           = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS,
-                .signal_type              = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
-                .supported_cache_ids      = _supported_cache_ids_ipx_address,
-                .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_ipx_address,
-                .cmd_plobj_id_copy        = _vt_cmd_plobj_id_copy_ip6_address,
-                .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_ip6_address,
-                .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address,
-                .cmd_plobj_to_string_id   = _vt_cmd_plobj_to_string_id_ip6_address,
-                .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string,
-                .cmd_plobj_hash_update =
-                    (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update,
-                .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp},
-    [NMP_OBJECT_TYPE_IP4_ROUTE - 1] =
-        {
-            .parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type                 = NMP_OBJECT_TYPE_IP4_ROUTE,
-            .sizeof_data              = sizeof(NMPObjectIP4Route),
-            .sizeof_public            = sizeof(NMPlatformIP4Route),
-            .obj_type_name            = "ip4-route",
-            .addr_family              = AF_INET,
-            .rtm_gettype              = RTM_GETROUTE,
-            .signal_type_id           = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE,
-            .signal_type              = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED,
-            .supported_cache_ids      = _supported_cache_ids_ipx_route,
-            .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_ipx_route,
-            .cmd_plobj_id_copy        = _vt_cmd_plobj_id_copy_ip4_route,
-            .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_ip4_route,
-            .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route,
-            .cmd_plobj_to_string_id   = (CmdPlobjToStringIdFunc) nm_platform_ip4_route_to_string,
-            .cmd_plobj_to_string      = (CmdPlobjToStringFunc) nm_platform_ip4_route_to_string,
-            .cmd_plobj_hash_update    = _vt_cmd_plobj_hash_update_ip4_route,
-            .cmd_plobj_cmp            = (CmdPlobjCmpFunc) nm_platform_ip4_route_cmp_full,
-        },
-    [NMP_OBJECT_TYPE_IP6_ROUTE - 1] =
-        {
-            .parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type                 = NMP_OBJECT_TYPE_IP6_ROUTE,
-            .sizeof_data              = sizeof(NMPObjectIP6Route),
-            .sizeof_public            = sizeof(NMPlatformIP6Route),
-            .obj_type_name            = "ip6-route",
-            .addr_family              = AF_INET6,
-            .rtm_gettype              = RTM_GETROUTE,
-            .signal_type_id           = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE,
-            .signal_type              = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED,
-            .supported_cache_ids      = _supported_cache_ids_ipx_route,
-            .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_ipx_route,
-            .cmd_plobj_id_copy        = _vt_cmd_plobj_id_copy_ip6_route,
-            .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_ip6_route,
-            .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_route,
-            .cmd_plobj_to_string_id   = (CmdPlobjToStringIdFunc) nm_platform_ip6_route_to_string,
-            .cmd_plobj_to_string      = (CmdPlobjToStringFunc) nm_platform_ip6_route_to_string,
-            .cmd_plobj_hash_update    = _vt_cmd_plobj_hash_update_ip6_route,
-            .cmd_plobj_cmp            = (CmdPlobjCmpFunc) nm_platform_ip6_route_cmp_full,
-        },
-    [NMP_OBJECT_TYPE_ROUTING_RULE - 1] =
-        {
-            .parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type                 = NMP_OBJECT_TYPE_ROUTING_RULE,
-            .sizeof_data              = sizeof(NMPObjectRoutingRule),
-            .sizeof_public            = sizeof(NMPlatformRoutingRule),
-            .obj_type_name            = "routing-rule",
-            .rtm_gettype              = RTM_GETRULE,
-            .signal_type_id           = NM_PLATFORM_SIGNAL_ID_ROUTING_RULE,
-            .signal_type              = NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED,
-            .supported_cache_ids      = _supported_cache_ids_routing_rules,
-            .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_routing_rule,
-            .cmd_plobj_id_copy        = _vt_cmd_plobj_id_copy_routing_rule,
-            .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_routing_rule,
-            .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_routing_rule,
-            .cmd_plobj_to_string_id   = (CmdPlobjToStringIdFunc) nm_platform_routing_rule_to_string,
-            .cmd_plobj_to_string      = (CmdPlobjToStringFunc) nm_platform_routing_rule_to_string,
-            .cmd_plobj_hash_update    = _vt_cmd_plobj_hash_update_routing_rule,
-            .cmd_plobj_cmp            = (CmdPlobjCmpFunc) nm_platform_routing_rule_cmp_full,
-        },
-    [NMP_OBJECT_TYPE_QDISC - 1] =
-        {
-            .parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type                 = NMP_OBJECT_TYPE_QDISC,
-            .sizeof_data              = sizeof(NMPObjectQdisc),
-            .sizeof_public            = sizeof(NMPlatformQdisc),
-            .obj_type_name            = "qdisc",
-            .rtm_gettype              = RTM_GETQDISC,
-            .signal_type_id           = NM_PLATFORM_SIGNAL_ID_QDISC,
-            .signal_type              = NM_PLATFORM_SIGNAL_QDISC_CHANGED,
-            .supported_cache_ids      = _supported_cache_ids_object,
-            .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_qdisc,
-            .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_qdisc,
-            .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_qdisc,
-            .cmd_plobj_to_string_id   = _vt_cmd_plobj_to_string_id_qdisc,
-            .cmd_plobj_to_string      = (CmdPlobjToStringFunc) nm_platform_qdisc_to_string,
-            .cmd_plobj_hash_update    = (CmdPlobjHashUpdateFunc) nm_platform_qdisc_hash_update,
-            .cmd_plobj_cmp            = (CmdPlobjCmpFunc) nm_platform_qdisc_cmp,
-        },
-    [NMP_OBJECT_TYPE_TFILTER - 1] =
-        {
-            .parent                   = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type                 = NMP_OBJECT_TYPE_TFILTER,
-            .sizeof_data              = sizeof(NMPObjectTfilter),
-            .sizeof_public            = sizeof(NMPlatformTfilter),
-            .obj_type_name            = "tfilter",
-            .rtm_gettype              = RTM_GETTFILTER,
-            .signal_type_id           = NM_PLATFORM_SIGNAL_ID_TFILTER,
-            .signal_type              = NM_PLATFORM_SIGNAL_TFILTER_CHANGED,
-            .supported_cache_ids      = _supported_cache_ids_object,
-            .cmd_obj_is_alive         = _vt_cmd_obj_is_alive_tfilter,
-            .cmd_plobj_id_cmp         = _vt_cmd_plobj_id_cmp_tfilter,
-            .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_tfilter,
-            .cmd_plobj_to_string_id   = _vt_cmd_plobj_to_string_id_tfilter,
-            .cmd_plobj_to_string      = (CmdPlobjToStringFunc) nm_platform_tfilter_to_string,
-            .cmd_plobj_hash_update    = (CmdPlobjHashUpdateFunc) nm_platform_tfilter_hash_update,
-            .cmd_plobj_cmp            = (CmdPlobjCmpFunc) nm_platform_tfilter_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_BRIDGE - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_BRIDGE,
-            .sizeof_data           = sizeof(NMPObjectLnkBridge),
-            .sizeof_public         = sizeof(NMPlatformLnkBridge),
-            .obj_type_name         = "bridge",
-            .lnk_link_type         = NM_LINK_TYPE_BRIDGE,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_bridge_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bridge_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_bridge_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_GRE - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_GRE,
-            .sizeof_data           = sizeof(NMPObjectLnkGre),
-            .sizeof_public         = sizeof(NMPlatformLnkGre),
-            .obj_type_name         = "gre",
-            .lnk_link_type         = NM_LINK_TYPE_GRE,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_gre_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_gre_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_gre_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_GRETAP - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_GRETAP,
-            .sizeof_data           = sizeof(NMPObjectLnkGre),
-            .sizeof_public         = sizeof(NMPlatformLnkGre),
-            .obj_type_name         = "gretap",
-            .lnk_link_type         = NM_LINK_TYPE_GRETAP,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_gre_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_gre_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_gre_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] =
-        {
-            .parent              = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type            = NMP_OBJECT_TYPE_LNK_INFINIBAND,
-            .sizeof_data         = sizeof(NMPObjectLnkInfiniband),
-            .sizeof_public       = sizeof(NMPlatformLnkInfiniband),
-            .obj_type_name       = "infiniband",
-            .lnk_link_type       = NM_LINK_TYPE_INFINIBAND,
-            .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_infiniband_to_string,
-            .cmd_plobj_hash_update =
-                (CmdPlobjHashUpdateFunc) nm_platform_lnk_infiniband_hash_update,
-            .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_infiniband_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_IP6TNL - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_IP6TNL,
-            .sizeof_data           = sizeof(NMPObjectLnkIp6Tnl),
-            .sizeof_public         = sizeof(NMPlatformLnkIp6Tnl),
-            .obj_type_name         = "ip6tnl",
-            .lnk_link_type         = NM_LINK_TYPE_IP6TNL,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_IP6GRE - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_IP6GRE,
-            .sizeof_data           = sizeof(NMPObjectLnkIp6Tnl),
-            .sizeof_public         = sizeof(NMPlatformLnkIp6Tnl),
-            .obj_type_name         = "ip6gre",
-            .lnk_link_type         = NM_LINK_TYPE_IP6GRE,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_IP6GRETAP - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_IP6GRETAP,
-            .sizeof_data           = sizeof(NMPObjectLnkIp6Tnl),
-            .sizeof_public         = sizeof(NMPlatformLnkIp6Tnl),
-            .obj_type_name         = "ip6gretap",
-            .lnk_link_type         = NM_LINK_TYPE_IP6GRETAP,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_IPIP - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_IPIP,
-            .sizeof_data           = sizeof(NMPObjectLnkIpIp),
-            .sizeof_public         = sizeof(NMPlatformLnkIpIp),
-            .obj_type_name         = "ipip",
-            .lnk_link_type         = NM_LINK_TYPE_IPIP,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_ipip_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ipip_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_ipip_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_MACSEC - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_MACSEC,
-            .sizeof_data           = sizeof(NMPObjectLnkMacsec),
-            .sizeof_public         = sizeof(NMPlatformLnkMacsec),
-            .obj_type_name         = "macsec",
-            .lnk_link_type         = NM_LINK_TYPE_MACSEC,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_macsec_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macsec_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_macsec_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_MACVLAN - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_MACVLAN,
-            .sizeof_data           = sizeof(NMPObjectLnkMacvlan),
-            .sizeof_public         = sizeof(NMPlatformLnkMacvlan),
-            .obj_type_name         = "macvlan",
-            .lnk_link_type         = NM_LINK_TYPE_MACVLAN,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_macvlan_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macvlan_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_macvlan_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_MACVTAP - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_MACVTAP,
-            .sizeof_data           = sizeof(NMPObjectLnkMacvtap),
-            .sizeof_public         = sizeof(NMPlatformLnkMacvlan),
-            .obj_type_name         = "macvtap",
-            .lnk_link_type         = NM_LINK_TYPE_MACVTAP,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_macvlan_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macvlan_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_macvlan_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_SIT - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_SIT,
-            .sizeof_data           = sizeof(NMPObjectLnkSit),
-            .sizeof_public         = sizeof(NMPlatformLnkSit),
-            .obj_type_name         = "sit",
-            .lnk_link_type         = NM_LINK_TYPE_SIT,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_sit_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_sit_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_sit_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_TUN - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_TUN,
-            .sizeof_data           = sizeof(NMPObjectLnkTun),
-            .sizeof_public         = sizeof(NMPlatformLnkTun),
-            .obj_type_name         = "tun",
-            .lnk_link_type         = NM_LINK_TYPE_TUN,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_tun_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_tun_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_tun_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_VLAN - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_VLAN,
-            .sizeof_data           = sizeof(NMPObjectLnkVlan),
-            .sizeof_public         = sizeof(NMPlatformLnkVlan),
-            .obj_type_name         = "vlan",
-            .lnk_link_type         = NM_LINK_TYPE_VLAN,
-            .cmd_obj_hash_update   = _vt_cmd_obj_hash_update_lnk_vlan,
-            .cmd_obj_cmp           = _vt_cmd_obj_cmp_lnk_vlan,
-            .cmd_obj_copy          = _vt_cmd_obj_copy_lnk_vlan,
-            .cmd_obj_dispose       = _vt_cmd_obj_dispose_lnk_vlan,
-            .cmd_obj_to_string     = _vt_cmd_obj_to_string_lnk_vlan,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_vlan_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vlan_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_vlan_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_VRF - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_VRF,
-            .sizeof_data           = sizeof(NMPObjectLnkVrf),
-            .sizeof_public         = sizeof(NMPlatformLnkVrf),
-            .obj_type_name         = "vrf",
-            .lnk_link_type         = NM_LINK_TYPE_VRF,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_vrf_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vrf_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_vrf_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_VXLAN - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_VXLAN,
-            .sizeof_data           = sizeof(NMPObjectLnkVxlan),
-            .sizeof_public         = sizeof(NMPlatformLnkVxlan),
-            .obj_type_name         = "vxlan",
-            .lnk_link_type         = NM_LINK_TYPE_VXLAN,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_vxlan_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vxlan_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_vxlan_cmp,
-        },
-    [NMP_OBJECT_TYPE_LNK_WIREGUARD - 1] =
-        {
-            .parent                = DEDUP_MULTI_OBJ_CLASS_INIT(),
-            .obj_type              = NMP_OBJECT_TYPE_LNK_WIREGUARD,
-            .sizeof_data           = sizeof(NMPObjectLnkWireGuard),
-            .sizeof_public         = sizeof(NMPlatformLnkWireGuard),
-            .obj_type_name         = "wireguard",
-            .lnk_link_type         = NM_LINK_TYPE_WIREGUARD,
-            .cmd_obj_hash_update   = _vt_cmd_obj_hash_update_lnk_wireguard,
-            .cmd_obj_cmp           = _vt_cmd_obj_cmp_lnk_wireguard,
-            .cmd_obj_copy          = _vt_cmd_obj_copy_lnk_wireguard,
-            .cmd_obj_dispose       = _vt_cmd_obj_dispose_lnk_wireguard,
-            .cmd_obj_to_string     = _vt_cmd_obj_to_string_lnk_wireguard,
-            .cmd_plobj_to_string   = (CmdPlobjToStringFunc) nm_platform_lnk_wireguard_to_string,
-            .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_wireguard_hash_update,
-            .cmd_plobj_cmp         = (CmdPlobjCmpFunc) nm_platform_lnk_wireguard_cmp,
-        },
-};
diff --git a/src/core/platform/nmp-object.h b/src/core/platform/nmp-object.h
deleted file mode 100644
index 19f6bcd7..00000000
--- a/src/core/platform/nmp-object.h
+++ /dev/null
@@ -1,1144 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2015 - 2018 Red Hat, Inc.
- */
-
-#ifndef __NMP_OBJECT_H__
-#define __NMP_OBJECT_H__
-
-#include <netinet/in.h>
-
-#include "nm-glib-aux/nm-obj.h"
-#include "nm-glib-aux/nm-dedup-multi.h"
-#include "nm-platform.h"
-
-struct udev_device;
-
-/*****************************************************************************/
-
-/* "struct __kernel_timespec" uses "long long", but we use gint64. In practice,
- * these are the same types. */
-G_STATIC_ASSERT(sizeof(long long) == sizeof(gint64));
-
-typedef struct {
-    /* like "struct __kernel_timespec". */
-    gint64 tv_sec;
-    gint64 tv_nsec;
-} NMPTimespec64;
-
-/*****************************************************************************/
-
-typedef union {
-    struct sockaddr     sa;
-    struct sockaddr_in  in;
-    struct sockaddr_in6 in6;
-} NMSockAddrUnion;
-
-G_STATIC_ASSERT(sizeof(NMSockAddrUnion) == sizeof(((NMSockAddrUnion *) NULL)->in6));
-
-/* we initialize the largest union member, to ensure that all fields are initialized. */
-
-#define NM_SOCK_ADDR_UNION_INIT_UNSPEC \
-    {                                  \
-        .in6 = {                       \
-            .sin6_family = AF_UNSPEC,  \
-        },                             \
-    }
-
-int nm_sock_addr_union_cmp(const NMSockAddrUnion *a, const NMSockAddrUnion *b);
-
-void nm_sock_addr_union_hash_update(const NMSockAddrUnion *a, NMHashState *h);
-
-void nm_sock_addr_union_cpy(NMSockAddrUnion *dst,
-                            gconstpointer    src /* unaligned (const NMSockAddrUnion *) */);
-
-void nm_sock_addr_union_cpy_untrusted(NMSockAddrUnion *dst,
-                                      gconstpointer src /* unaligned (const NMSockAddrUnion *) */,
-                                      gsize         src_len);
-
-const char *nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len);
-
-/*****************************************************************************/
-
-typedef struct {
-    NMIPAddr addr;
-    guint8   family;
-    guint8   mask;
-} NMPWireGuardAllowedIP;
-
-typedef struct _NMPWireGuardPeer {
-    NMSockAddrUnion endpoint;
-
-    NMPTimespec64 last_handshake_time;
-
-    guint64 rx_bytes;
-    guint64 tx_bytes;
-
-    union {
-        const NMPWireGuardAllowedIP *allowed_ips;
-        guint                        _construct_idx_start;
-    };
-    union {
-        guint allowed_ips_len;
-        guint _construct_idx_end;
-    };
-
-    guint16 persistent_keepalive_interval;
-
-    guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
-    guint8 preshared_key[NMP_WIREGUARD_SYMMETRIC_KEY_LEN];
-} NMPWireGuardPeer;
-
-/*****************************************************************************/
-
-typedef enum { /*< skip >*/
-               NMP_OBJECT_TO_STRING_ID,
-               NMP_OBJECT_TO_STRING_PUBLIC,
-               NMP_OBJECT_TO_STRING_ALL,
-} NMPObjectToStringMode;
-
-typedef enum { /*< skip >*/
-               NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE,
-               NMP_CACHE_OPS_ADDED     = NM_PLATFORM_SIGNAL_ADDED,
-               NMP_CACHE_OPS_UPDATED   = NM_PLATFORM_SIGNAL_CHANGED,
-               NMP_CACHE_OPS_REMOVED   = NM_PLATFORM_SIGNAL_REMOVED,
-} NMPCacheOpsType;
-
-/* The NMPCacheIdType are the different index types.
- *
- * An object of a certain object-type, can be candidate to being
- * indexed by a certain NMPCacheIdType or not. For example, all
- * objects are indexed via an index of type NMP_CACHE_ID_TYPE_OBJECT_TYPE,
- * but only route objects can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT.
- *
- * Of one index type, there can be multiple indexes or not.
- * For example, of the index type NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX there
- * are multiple instances (for different route/addresses, v4/v6, per-ifindex).
- *
- * But one object, can only be indexed by one particular index of a
- * type. For example, a certain address instance is only indexed by
- * the index NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX with
- * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible.
- * */
-typedef enum { /*< skip >*/
-               NMP_CACHE_ID_TYPE_NONE,
-
-               /* all the objects of a certain type.
-     *
-     * This index is special. It is the only one that contains *all* object.
-     * Other indexes may consider some object as non "partitionable", hence
-     * they don't track all objects.
-     *
-     * Hence, this index type is used when looking at all objects (still
-     * partitioned by type).
-     *
-     * Also, note that links may be considered invisible. This index type
-     * expose all links, even invisible ones. For addresses/routes, this
-     * distinction doesn't exist, as all addresses/routes that are alive
-     * are visible as well. */
-               NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-
-               /* index for the link objects by ifname. */
-               NMP_CACHE_ID_TYPE_LINK_BY_IFNAME,
-
-               /* indices for the visible default-routes, ignoring ifindex.
-     * This index only contains two partitions: all visible default-routes,
-     * separate for IPv4 and IPv6. */
-               NMP_CACHE_ID_TYPE_DEFAULT_ROUTES,
-
-               /* all the objects that have an ifindex (by object-type) for an ifindex. */
-               NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
-
-               /* Consider all the destination fields of a route, that is, the ID without the ifindex
-     * and gateway (meaning: network/plen,metric).
-     * The reason for this is that `ip route change` can replace an existing route
-     * and modify its ifindex/gateway. Effectively, that means it deletes an existing
-     * route and adds a different one (as the ID of the route changes). However, it only
-     * sends one RTM_NEWADDR notification without notifying about the deletion. We detect
-     * that by having this index to contain overlapping routes which require special
-     * cache-resync. */
-               NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
-
-               /* a filter for objects that track an explicit address family.
-     *
-     * Note that currently on NMPObjectRoutingRule is indexed by this filter. */
-               NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY,
-
-               __NMP_CACHE_ID_TYPE_MAX,
-               NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1,
-} NMPCacheIdType;
-
-typedef struct {
-    NMDedupMultiObjClass   parent;
-    const char *           obj_type_name;
-    const char *           signal_type;
-    const guint8 *         supported_cache_ids;
-    int                    sizeof_data;
-    int                    sizeof_public;
-    int                    addr_family;
-    int                    rtm_gettype;
-    NMPObjectType          obj_type;
-    NMPlatformSignalIdType signal_type_id;
-
-    /* Only for NMPObjectLnk* types. */
-    NMLinkType lnk_link_type;
-
-    void (*cmd_obj_hash_update)(const NMPObject *obj, NMHashState *h);
-    int (*cmd_obj_cmp)(const NMPObject *obj1, const NMPObject *obj2);
-    void (*cmd_obj_copy)(NMPObject *dst, const NMPObject *src);
-    void (*cmd_obj_dispose)(NMPObject *obj);
-    gboolean (*cmd_obj_is_alive)(const NMPObject *obj);
-    gboolean (*cmd_obj_is_visible)(const NMPObject *obj);
-    const char *(*cmd_obj_to_string)(const NMPObject *     obj,
-                                     NMPObjectToStringMode to_string_mode,
-                                     char *                buf,
-                                     gsize                 buf_size);
-
-    /* functions that operate on NMPlatformObject */
-    void (*cmd_plobj_id_copy)(NMPlatformObject *dst, const NMPlatformObject *src);
-    int (*cmd_plobj_id_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2);
-    void (*cmd_plobj_id_hash_update)(const NMPlatformObject *obj, NMHashState *h);
-    const char *(*cmd_plobj_to_string_id)(const NMPlatformObject *obj, char *buf, gsize buf_size);
-    const char *(*cmd_plobj_to_string)(const NMPlatformObject *obj, char *buf, gsize len);
-    void (*cmd_plobj_hash_update)(const NMPlatformObject *obj, NMHashState *h);
-    int (*cmd_plobj_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2);
-} NMPClass;
-
-extern const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX];
-
-typedef struct {
-    NMPlatformLink _public;
-
-    struct {
-        bool is_in_netlink;
-
-        /* Additional data that depends on the link-type (IFLA_INFO_DATA) */
-        const NMPObject *lnk;
-    } netlink;
-
-    struct {
-        /* note that "struct udev_device" references the library context
-         * "struct udev", but doesn't own it.
-         *
-         * Hence, the udev.device shall not be used after the library
-         * context is destroyed.
-         *
-         * In case of NMPObjectLink instances that you obtained from the
-         * platform cache, that means that you shall no keep references
-         * to those instances that outlife the NMPlatform instance.
-         *
-         * In practice, the requirement is less strict and you'll be even
-         * fine if the platform instance (and the "struct udev" instance)
-         * are already destroyed while you still hold onto a reference to
-         * the NMPObjectLink instance. Just don't make use of udev functions
-         * that cause access to the udev library context.
-         */
-        struct udev_device *device;
-    } udev;
-
-    /* Auxiliary data object for Wi-Fi and WPAN */
-    GObject *ext_data;
-
-    /* FIXME: not every NMPObjectLink should pay the price for tracking
-     * the wireguard family id. This should be tracked via ext_data, which
-     * would be exactly the right place. */
-    int wireguard_family_id;
-} NMPObjectLink;
-
-typedef struct {
-    NMPlatformLnkBridge _public;
-} NMPObjectLnkBridge;
-
-typedef struct {
-    NMPlatformLnkGre _public;
-} NMPObjectLnkGre;
-
-typedef struct {
-    NMPlatformLnkInfiniband _public;
-} NMPObjectLnkInfiniband;
-
-typedef struct {
-    NMPlatformLnkIp6Tnl _public;
-} NMPObjectLnkIp6Tnl;
-
-typedef struct {
-    NMPlatformLnkIpIp _public;
-} NMPObjectLnkIpIp;
-
-typedef struct {
-    NMPlatformLnkMacsec _public;
-} NMPObjectLnkMacsec;
-
-typedef struct {
-    NMPlatformLnkMacvlan _public;
-} NMPObjectLnkMacvlan;
-
-typedef NMPObjectLnkMacvlan NMPObjectLnkMacvtap;
-
-typedef struct {
-    NMPlatformLnkSit _public;
-} NMPObjectLnkSit;
-
-typedef struct {
-    NMPlatformLnkTun _public;
-} NMPObjectLnkTun;
-
-typedef struct {
-    NMPlatformLnkVlan _public;
-
-    guint                   n_ingress_qos_map;
-    guint                   n_egress_qos_map;
-    const NMVlanQosMapping *ingress_qos_map;
-    const NMVlanQosMapping *egress_qos_map;
-} NMPObjectLnkVlan;
-
-typedef struct {
-    NMPlatformLnkVrf _public;
-} NMPObjectLnkVrf;
-
-typedef struct {
-    NMPlatformLnkVxlan _public;
-} NMPObjectLnkVxlan;
-
-typedef struct {
-    NMPlatformLnkWireGuard       _public;
-    const NMPWireGuardPeer *     peers;
-    const NMPWireGuardAllowedIP *_allowed_ips_buf;
-    guint                        peers_len;
-    guint                        _allowed_ips_buf_len;
-} NMPObjectLnkWireGuard;
-
-typedef struct {
-    NMPlatformIP4Address _public;
-} NMPObjectIP4Address;
-
-typedef struct {
-    NMPlatformIP4Route _public;
-} NMPObjectIP4Route;
-
-typedef struct {
-    NMPlatformIP6Address _public;
-} NMPObjectIP6Address;
-
-typedef struct {
-    NMPlatformIP6Route _public;
-} NMPObjectIP6Route;
-
-typedef struct {
-    NMPlatformRoutingRule _public;
-} NMPObjectRoutingRule;
-
-typedef struct {
-    NMPlatformQdisc _public;
-} NMPObjectQdisc;
-
-typedef struct {
-    NMPlatformTfilter _public;
-} NMPObjectTfilter;
-
-struct _NMPObject {
-    union {
-        NMDedupMultiObj parent;
-        const NMPClass *_class;
-    };
-    union {
-        NMPlatformObject object;
-
-        NMPlatformObjWithIfindex obj_with_ifindex;
-
-        NMPlatformLink link;
-        NMPObjectLink  _link;
-
-        NMPlatformLnkBridge lnk_bridge;
-        NMPObjectLnkBridge  _lnk_bridge;
-
-        NMPlatformLnkGre lnk_gre;
-        NMPObjectLnkGre  _lnk_gre;
-
-        NMPlatformLnkInfiniband lnk_infiniband;
-        NMPObjectLnkInfiniband  _lnk_infiniband;
-
-        NMPlatformLnkIpIp lnk_ipip;
-        NMPObjectLnkIpIp  _lnk_ipip;
-
-        NMPlatformLnkIp6Tnl lnk_ip6tnl;
-        NMPObjectLnkIp6Tnl  _lnk_ip6tnl;
-
-        NMPlatformLnkMacsec lnk_macsec;
-        NMPObjectLnkMacsec  _lnk_macsec;
-
-        NMPlatformLnkMacvlan lnk_macvlan;
-        NMPObjectLnkMacvlan  _lnk_macvlan;
-
-        NMPlatformLnkSit lnk_sit;
-        NMPObjectLnkSit  _lnk_sit;
-
-        NMPlatformLnkTun lnk_tun;
-        NMPObjectLnkTun  _lnk_tun;
-
-        NMPlatformLnkVlan lnk_vlan;
-        NMPObjectLnkVlan  _lnk_vlan;
-
-        NMPlatformLnkVrf lnk_vrf;
-        NMPObjectLnkVrf  _lnk_vrf;
-
-        NMPlatformLnkVxlan lnk_vxlan;
-        NMPObjectLnkVxlan  _lnk_vxlan;
-
-        NMPlatformLnkWireGuard lnk_wireguard;
-        NMPObjectLnkWireGuard  _lnk_wireguard;
-
-        NMPlatformIPAddress  ip_address;
-        NMPlatformIPXAddress ipx_address;
-        NMPlatformIP4Address ip4_address;
-        NMPlatformIP6Address ip6_address;
-        NMPObjectIP4Address  _ip4_address;
-        NMPObjectIP6Address  _ip6_address;
-
-        NMPlatformIPRoute  ip_route;
-        NMPlatformIPXRoute ipx_route;
-        NMPlatformIP4Route ip4_route;
-        NMPlatformIP6Route ip6_route;
-        NMPObjectIP4Route  _ip4_route;
-        NMPObjectIP6Route  _ip6_route;
-
-        NMPlatformRoutingRule routing_rule;
-        NMPObjectRoutingRule  _routing_rule;
-
-        NMPlatformQdisc   qdisc;
-        NMPObjectQdisc    _qdisc;
-        NMPlatformTfilter tfilter;
-        NMPObjectTfilter  _tfilter;
-    };
-};
-
-/*****************************************************************************/
-
-static inline gboolean
-NMP_CLASS_IS_VALID(const NMPClass *klass)
-{
-    return klass >= &_nmp_classes[0] && klass <= &_nmp_classes[G_N_ELEMENTS(_nmp_classes)]
-           && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof(_nmp_classes[0]))) == 0;
-}
-
-static inline const NMPClass *
-nmp_class_from_type(NMPObjectType obj_type)
-{
-    nm_assert(obj_type > 0);
-    nm_assert(obj_type <= G_N_ELEMENTS(_nmp_classes));
-    nm_assert(_nmp_classes[obj_type - 1].obj_type == obj_type);
-    nm_assert(NMP_CLASS_IS_VALID(&_nmp_classes[obj_type - 1]));
-
-    return &_nmp_classes[obj_type - 1];
-}
-
-static inline NMPObject *
-NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj)
-{
-    NMPObject *obj;
-
-    obj = plobj ? (NMPObject *) (&(((char *) plobj)[-((int) G_STRUCT_OFFSET(NMPObject, object))]))
-                : NULL;
-    nm_assert(!obj || (obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID(obj->_class)));
-    return obj;
-}
-#define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST((const NMPlatformObject *) (plobj)))
-
-static inline gboolean
-NMP_OBJECT_IS_VALID(const NMPObject *obj)
-{
-    nm_assert(!obj || (obj && obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID(obj->_class)));
-
-    /* There isn't really much to check. Either @obj is NULL, or we must
-     * assume that it points to valid memory. */
-    return obj != NULL;
-}
-
-static inline gboolean
-NMP_OBJECT_IS_STACKINIT(const NMPObject *obj)
-{
-    nm_assert(!obj || NMP_OBJECT_IS_VALID(obj));
-
-    return obj && obj->parent._ref_count == NM_OBJ_REF_COUNT_STACKINIT;
-}
-
-static inline const NMPClass *
-NMP_OBJECT_GET_CLASS(const NMPObject *obj)
-{
-    nm_assert(NMP_OBJECT_IS_VALID(obj));
-
-    return obj->_class;
-}
-
-static inline NMPObjectType
-NMP_OBJECT_GET_TYPE(const NMPObject *obj)
-{
-    nm_assert(!obj || NMP_OBJECT_IS_VALID(obj));
-
-    return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN;
-}
-
-static inline gboolean
-_NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
-{
-    switch (obj_type) {
-    case NMP_OBJECT_TYPE_LINK:
-    case NMP_OBJECT_TYPE_IP4_ADDRESS:
-    case NMP_OBJECT_TYPE_IP6_ADDRESS:
-    case NMP_OBJECT_TYPE_IP4_ROUTE:
-    case NMP_OBJECT_TYPE_IP6_ROUTE:
-
-    case NMP_OBJECT_TYPE_QDISC:
-
-    case NMP_OBJECT_TYPE_TFILTER:
-
-    case NMP_OBJECT_TYPE_LNK_BRIDGE:
-    case NMP_OBJECT_TYPE_LNK_GRE:
-    case NMP_OBJECT_TYPE_LNK_GRETAP:
-    case NMP_OBJECT_TYPE_LNK_INFINIBAND:
-    case NMP_OBJECT_TYPE_LNK_IP6TNL:
-    case NMP_OBJECT_TYPE_LNK_IP6GRE:
-    case NMP_OBJECT_TYPE_LNK_IP6GRETAP:
-    case NMP_OBJECT_TYPE_LNK_IPIP:
-    case NMP_OBJECT_TYPE_LNK_MACSEC:
-    case NMP_OBJECT_TYPE_LNK_MACVLAN:
-    case NMP_OBJECT_TYPE_LNK_MACVTAP:
-    case NMP_OBJECT_TYPE_LNK_SIT:
-    case NMP_OBJECT_TYPE_LNK_TUN:
-    case NMP_OBJECT_TYPE_LNK_VLAN:
-    case NMP_OBJECT_TYPE_LNK_VRF:
-    case NMP_OBJECT_TYPE_LNK_VXLAN:
-    case NMP_OBJECT_TYPE_LNK_WIREGUARD:
-        return TRUE;
-
-    case NMP_OBJECT_TYPE_ROUTING_RULE:
-        return FALSE;
-
-    case NMP_OBJECT_TYPE_UNKNOWN:
-    case __NMP_OBJECT_TYPE_LAST:
-        break;
-    }
-    nm_assert_not_reached();
-    return FALSE;
-}
-
-#define NMP_OBJECT_CAST_OBJECT(obj)                                       \
-    ({                                                                    \
-        typeof(obj) _obj = (obj);                                         \
-                                                                          \
-        nm_assert (   !_obj \
-                   || nmp_class_from_type (NMP_OBJECT_GET_TYPE (_obj)))); \
-        _obj ? &NM_CONSTCAST(NMPObject, _obj)->object : NULL;             \
-    })
-
-#define NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)                                                \
-    ({                                                                                       \
-        typeof(obj) _obj = (obj);                                                            \
-                                                                                             \
-        nm_assert(!_obj || _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMP_OBJECT_GET_TYPE(_obj))); \
-        _obj ? &NM_CONSTCAST(NMPObject, _obj)->obj_with_ifindex : NULL;                      \
-    })
-
-#define _NMP_OBJECT_CAST(obj, field, ...)                                      \
-    ({                                                                         \
-        typeof(obj) _obj = (obj);                                              \
-                                                                               \
-        nm_assert(!_obj || NM_IN_SET(NMP_OBJECT_GET_TYPE(_obj), __VA_ARGS__)); \
-        _obj ? &NM_CONSTCAST(NMPObject, _obj)->field : NULL;                   \
-    })
-
-#define NMP_OBJECT_CAST_LINK(obj) _NMP_OBJECT_CAST(obj, link, NMP_OBJECT_TYPE_LINK)
-#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \
-    _NMP_OBJECT_CAST(obj, ip_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)
-#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \
-    _NMP_OBJECT_CAST(obj, ipx_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)
-#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \
-    _NMP_OBJECT_CAST(obj, ip4_address, NMP_OBJECT_TYPE_IP4_ADDRESS)
-#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \
-    _NMP_OBJECT_CAST(obj, ip6_address, NMP_OBJECT_TYPE_IP6_ADDRESS)
-#define NMP_OBJECT_CAST_IP_ROUTE(obj) \
-    _NMP_OBJECT_CAST(obj, ip_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)
-#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \
-    _NMP_OBJECT_CAST(obj, ipx_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)
-#define NMP_OBJECT_CAST_IP4_ROUTE(obj) _NMP_OBJECT_CAST(obj, ip4_route, NMP_OBJECT_TYPE_IP4_ROUTE)
-#define NMP_OBJECT_CAST_IP6_ROUTE(obj) _NMP_OBJECT_CAST(obj, ip6_route, NMP_OBJECT_TYPE_IP6_ROUTE)
-#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \
-    _NMP_OBJECT_CAST(obj, routing_rule, NMP_OBJECT_TYPE_ROUTING_RULE)
-#define NMP_OBJECT_CAST_QDISC(obj)   _NMP_OBJECT_CAST(obj, qdisc, NMP_OBJECT_TYPE_QDISC)
-#define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST(obj, tfilter, NMP_OBJECT_TYPE_TFILTER)
-#define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) \
-    _NMP_OBJECT_CAST(obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD)
-#define NMP_OBJECT_CAST_LNK_BRIDGE(obj) \
-    _NMP_OBJECT_CAST(obj, lnk_bridge, NMP_OBJECT_TYPE_LNK_BRIDGE)
-
-static inline int
-NMP_OBJECT_TYPE_TO_ADDR_FAMILY(NMPObjectType obj_type)
-{
-    return nmp_class_from_type(obj_type)->addr_family;
-}
-
-static inline int
-NMP_OBJECT_GET_ADDR_FAMILY(const NMPObject *obj)
-{
-    return NMP_OBJECT_GET_CLASS(obj)->addr_family;
-}
-
-static inline const NMPObject *
-nmp_object_ref(const NMPObject *obj)
-{
-    if (!obj) {
-        /* for convenience, allow NULL. */
-        return NULL;
-    }
-
-    /* ref and unref accept const pointers. NMPObject is supposed to be shared
-     * and kept immutable. Disallowing to take/return a reference to a const
-     * NMPObject is cumbersome, because callers are precisely expected to
-     * keep a ref on the otherwise immutable object. */
-    g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj), NULL);
-    g_return_val_if_fail(obj->parent._ref_count != NM_OBJ_REF_COUNT_STACKINIT, NULL);
-
-    return (const NMPObject *) nm_dedup_multi_obj_ref((const NMDedupMultiObj *) obj);
-}
-
-static inline void
-nmp_object_unref(const NMPObject *obj)
-{
-    if (obj) {
-        nm_assert(NMP_OBJECT_IS_VALID(obj));
-
-        nm_dedup_multi_obj_unref((const NMDedupMultiObj *) obj);
-    }
-}
-
-#define nm_clear_nmp_object(ptr)       \
-    ({                                 \
-        typeof(ptr) _ptr = (ptr);      \
-        typeof(*_ptr) _pptr;           \
-        gboolean _changed = FALSE;     \
-                                       \
-        if (_ptr && (_pptr = *_ptr)) { \
-            *_ptr = NULL;              \
-            nmp_object_unref(_pptr);   \
-            _changed = TRUE;           \
-        }                              \
-        _changed;                      \
-    })
-
-static inline gboolean
-nmp_object_ref_set(const NMPObject **pp, const NMPObject *obj)
-{
-    gboolean         _changed = FALSE;
-    const NMPObject *p;
-
-    nm_assert(!pp || !*pp || NMP_OBJECT_IS_VALID(*pp));
-    nm_assert(!obj || NMP_OBJECT_IS_VALID(obj));
-
-    if (pp && ((p = *pp) != obj)) {
-        nmp_object_ref(obj);
-        *pp = obj;
-        nmp_object_unref(p);
-        _changed = TRUE;
-    }
-    return _changed;
-}
-
-NMPObject *nmp_object_new(NMPObjectType obj_type, gconstpointer plobj);
-NMPObject *nmp_object_new_link(int ifindex);
-
-const NMPObject *nmp_object_stackinit(NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj);
-
-static inline NMPObject *
-nmp_object_stackinit_obj(NMPObject *obj, const NMPObject *src)
-{
-    return obj == src
-               ? obj
-               : (NMPObject *) nmp_object_stackinit(obj, NMP_OBJECT_GET_TYPE(src), &src->object);
-}
-
-const NMPObject *nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src);
-const NMPObject *nmp_object_stackinit_id_link(NMPObject *obj, int ifindex);
-const NMPObject *nmp_object_stackinit_id_ip4_address(NMPObject *obj,
-                                                     int        ifindex,
-                                                     guint32    address,
-                                                     guint8     plen,
-                                                     guint32    peer_address);
-const NMPObject *
-nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in6_addr *address);
-
-const char *nmp_object_to_string(const NMPObject *     obj,
-                                 NMPObjectToStringMode to_string_mode,
-                                 char *                buf,
-                                 gsize                 buf_size);
-void        nmp_object_hash_update(const NMPObject *obj, NMHashState *h);
-int         nmp_object_cmp(const NMPObject *obj1, const NMPObject *obj2);
-
-static inline gboolean
-nmp_object_equal(const NMPObject *obj1, const NMPObject *obj2)
-{
-    return nmp_object_cmp(obj1, obj2) == 0;
-}
-
-void       nmp_object_copy(NMPObject *dst, const NMPObject *src, gboolean id_only);
-NMPObject *nmp_object_clone(const NMPObject *obj, gboolean id_only);
-
-int   nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2);
-void  nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h);
-guint nmp_object_id_hash(const NMPObject *obj);
-
-static inline gboolean
-nmp_object_id_equal(const NMPObject *obj1, const NMPObject *obj2)
-{
-    return nmp_object_id_cmp(obj1, obj2) == 0;
-}
-
-guint    nmp_object_indirect_id_hash(gconstpointer a);
-gboolean nmp_object_indirect_id_equal(gconstpointer a, gconstpointer b);
-
-gboolean nmp_object_is_alive(const NMPObject *obj);
-gboolean nmp_object_is_visible(const NMPObject *obj);
-
-void
-_nmp_object_fixup_link_udev_fields(NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev);
-
-static inline void
-_nm_auto_nmpobj_cleanup(gpointer p)
-{
-    nmp_object_unref(*((const NMPObject **) p));
-}
-#define nm_auto_nmpobj nm_auto(_nm_auto_nmpobj_cleanup)
-
-typedef struct _NMPCache NMPCache;
-
-typedef void (*NMPCachePreHook)(NMPCache *       cache,
-                                const NMPObject *old,
-                                const NMPObject *new,
-                                NMPCacheOpsType ops_type,
-                                gpointer        user_data);
-typedef gboolean (*NMPObjectMatchFn)(const NMPObject *obj, gpointer user_data);
-
-const NMDedupMultiEntry *nmp_cache_lookup_entry(const NMPCache *cache, const NMPObject *obj);
-const NMDedupMultiEntry *nmp_cache_lookup_entry_with_idx_type(const NMPCache * cache,
-                                                              NMPCacheIdType   cache_id_type,
-                                                              const NMPObject *obj);
-const NMDedupMultiEntry *nmp_cache_lookup_entry_link(const NMPCache *cache, int ifindex);
-const NMPObject *        nmp_cache_lookup_obj(const NMPCache *cache, const NMPObject *obj);
-const NMPObject *        nmp_cache_lookup_link(const NMPCache *cache, int ifindex);
-
-typedef struct _NMPLookup NMPLookup;
-
-struct _NMPLookup {
-    NMPCacheIdType cache_id_type;
-    NMPObject      selector_obj;
-};
-
-const NMDedupMultiHeadEntry *nmp_cache_lookup_all(const NMPCache * cache,
-                                                  NMPCacheIdType   cache_id_type,
-                                                  const NMPObject *select_obj);
-
-static inline const NMDedupMultiHeadEntry *
-nmp_cache_lookup(const NMPCache *cache, const NMPLookup *lookup)
-{
-    return nmp_cache_lookup_all(cache, lookup->cache_id_type, &lookup->selector_obj);
-}
-
-const NMPLookup *nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type);
-const NMPLookup *nmp_lookup_init_link_by_ifname(NMPLookup *lookup, const char *ifname);
-const NMPLookup *nmp_lookup_init_object(NMPLookup *lookup, NMPObjectType obj_type, int ifindex);
-const NMPLookup *nmp_lookup_init_route_default(NMPLookup *lookup, NMPObjectType obj_type);
-const NMPLookup *nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj);
-const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup,
-                                                      in_addr_t  network,
-                                                      guint      plen,
-                                                      guint32    metric,
-                                                      guint8     tos);
-const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id(NMPLookup *            lookup,
-                                                      const struct in6_addr *network,
-                                                      guint                  plen,
-                                                      guint32                metric,
-                                                      const struct in6_addr *src,
-                                                      guint8                 src_plen);
-const NMPLookup *
-nmp_lookup_init_object_by_addr_family(NMPLookup *lookup, NMPObjectType obj_type, int addr_family);
-
-GArray *nmp_cache_lookup_to_array(const NMDedupMultiHeadEntry *head_entry,
-                                  NMPObjectType                obj_type,
-                                  gboolean                     visible_only);
-
-static inline gboolean
-nmp_cache_iter_next(NMDedupMultiIter *iter, const NMPObject **out_obj)
-{
-    gboolean has_next;
-
-    has_next = nm_dedup_multi_iter_next(iter);
-    nm_assert(!has_next || NMP_OBJECT_IS_VALID(iter->current->obj));
-    if (out_obj)
-        *out_obj = has_next ? iter->current->obj : NULL;
-    return has_next;
-}
-
-static inline gboolean
-nmp_cache_iter_next_link(NMDedupMultiIter *iter, const NMPlatformLink **out_obj)
-{
-    gboolean has_next;
-
-    has_next = nm_dedup_multi_iter_next(iter);
-    nm_assert(!has_next || NMP_OBJECT_GET_TYPE(iter->current->obj) == NMP_OBJECT_TYPE_LINK);
-    if (out_obj)
-        *out_obj = has_next ? &(((const NMPObject *) iter->current->obj)->link) : NULL;
-    return has_next;
-}
-
-#define nmp_cache_iter_for_each(iter, head, obj) \
-    for (nm_dedup_multi_iter_init((iter), (head)); nmp_cache_iter_next((iter), (obj));)
-
-#define nmp_cache_iter_for_each_link(iter, head, obj) \
-    for (nm_dedup_multi_iter_init((iter), (head)); nmp_cache_iter_next_link((iter), (obj));)
-
-const NMPObject *nmp_cache_lookup_link_full(const NMPCache * cache,
-                                            int              ifindex,
-                                            const char *     ifname,
-                                            gboolean         visible_only,
-                                            NMLinkType       link_type,
-                                            NMPObjectMatchFn match_fn,
-                                            gpointer         user_data);
-
-gboolean         nmp_cache_link_connected_for_slave(int ifindex_master, const NMPObject *slave);
-gboolean         nmp_cache_link_connected_needs_toggle(const NMPCache * cache,
-                                                       const NMPObject *master,
-                                                       const NMPObject *potential_slave,
-                                                       const NMPObject *ignore_slave);
-const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex(const NMPCache * cache,
-                                                                  int              master_ifindex,
-                                                                  const NMPObject *potential_slave,
-                                                                  const NMPObject *ignore_slave);
-
-gboolean nmp_cache_use_udev_get(const NMPCache *cache);
-
-void nmtst_assert_nmp_cache_is_consistent(const NMPCache *cache);
-
-NMPCacheOpsType nmp_cache_remove(NMPCache *        cache,
-                                 const NMPObject * obj_needle,
-                                 gboolean          equals_by_ptr,
-                                 gboolean          only_dirty,
-                                 const NMPObject **out_obj_old);
-NMPCacheOpsType nmp_cache_remove_netlink(NMPCache *        cache,
-                                         const NMPObject * obj_needle,
-                                         const NMPObject **out_obj_old,
-                                         const NMPObject **out_obj_new);
-NMPCacheOpsType nmp_cache_update_netlink(NMPCache *        cache,
-                                         NMPObject *       obj_hand_over,
-                                         gboolean          is_dump,
-                                         const NMPObject **out_obj_old,
-                                         const NMPObject **out_obj_new);
-NMPCacheOpsType nmp_cache_update_netlink_route(NMPCache *        cache,
-                                               NMPObject *       obj_hand_over,
-                                               gboolean          is_dump,
-                                               guint16           nlmsgflags,
-                                               const NMPObject **out_obj_old,
-                                               const NMPObject **out_obj_new,
-                                               const NMPObject **out_obj_replace,
-                                               gboolean *        out_resync_required);
-NMPCacheOpsType nmp_cache_update_link_udev(NMPCache *          cache,
-                                           int                 ifindex,
-                                           struct udev_device *udevice,
-                                           const NMPObject **  out_obj_old,
-                                           const NMPObject **  out_obj_new);
-NMPCacheOpsType nmp_cache_update_link_master_connected(NMPCache *        cache,
-                                                       int               ifindex,
-                                                       const NMPObject **out_obj_old,
-                                                       const NMPObject **out_obj_new);
-
-static inline const NMDedupMultiEntry *
-nmp_cache_reresolve_main_entry(NMPCache *               cache,
-                               const NMDedupMultiEntry *entry,
-                               const NMPLookup *        lookup)
-{
-    const NMDedupMultiEntry *main_entry;
-
-    nm_assert(cache);
-    nm_assert(entry);
-    nm_assert(lookup);
-
-    if (lookup->cache_id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) {
-        nm_assert(entry == nmp_cache_lookup_entry(cache, entry->obj));
-        return entry;
-    }
-
-    /* we only track the dirty flag for the OBJECT-TYPE index. That means,
-     * for other lookup types we need to check the dirty flag of the main-entry. */
-    main_entry = nmp_cache_lookup_entry(cache, entry->obj);
-
-    nm_assert(main_entry);
-    nm_assert(main_entry->obj == entry->obj);
-
-    return main_entry;
-}
-
-void nmp_cache_dirty_set_all_main(NMPCache *cache, const NMPLookup *lookup);
-
-NMPCache *nmp_cache_new(NMDedupMultiIndex *multi_idx, gboolean use_udev);
-void      nmp_cache_free(NMPCache *cache);
-
-static inline void
-ASSERT_nmp_cache_ops(const NMPCache * cache,
-                     NMPCacheOpsType  ops_type,
-                     const NMPObject *obj_old,
-                     const NMPObject *obj_new)
-{
-#if NM_MORE_ASSERTS
-    nm_assert(cache);
-    nm_assert(obj_old || obj_new);
-    nm_assert(!obj_old
-              || (NMP_OBJECT_IS_VALID(obj_old) && !NMP_OBJECT_IS_STACKINIT(obj_old)
-                  && nmp_object_is_alive(obj_old)));
-    nm_assert(!obj_new
-              || (NMP_OBJECT_IS_VALID(obj_new) && !NMP_OBJECT_IS_STACKINIT(obj_new)
-                  && nmp_object_is_alive(obj_new)));
-
-    switch (ops_type) {
-    case NMP_CACHE_OPS_UNCHANGED:
-        nm_assert(obj_old == obj_new);
-        break;
-    case NMP_CACHE_OPS_ADDED:
-        nm_assert(!obj_old && obj_new);
-        break;
-    case NMP_CACHE_OPS_UPDATED:
-        nm_assert(obj_old && obj_new && obj_old != obj_new);
-        break;
-    case NMP_CACHE_OPS_REMOVED:
-        nm_assert(obj_old && !obj_new);
-        break;
-    default:
-        nm_assert_not_reached();
-    }
-
-    nm_assert(obj_new == NULL || obj_old == NULL || nmp_object_id_equal(obj_new, obj_old));
-    nm_assert(!obj_old || !obj_new
-              || NMP_OBJECT_GET_CLASS(obj_old) == NMP_OBJECT_GET_CLASS(obj_new));
-
-    nm_assert(obj_new == nmp_cache_lookup_obj(cache, obj_new ?: obj_old));
-#endif
-}
-
-const NMDedupMultiHeadEntry *
-nm_platform_lookup_all(NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj);
-
-const NMDedupMultiEntry *
-nm_platform_lookup_entry(NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj);
-
-static inline const NMPObject *
-nm_platform_lookup_obj(NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj)
-{
-    return nm_dedup_multi_entry_get_obj(nm_platform_lookup_entry(platform, cache_id_type, obj));
-}
-
-static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_obj_type(NMPlatform *platform, NMPObjectType obj_type)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_obj_type(&lookup, obj_type);
-    return nm_platform_lookup(platform, &lookup);
-}
-
-static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_link_by_ifname(NMPlatform *platform, const char *ifname)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_link_by_ifname(&lookup, ifname);
-    return nm_platform_lookup(platform, &lookup);
-}
-
-static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_object(NMPlatform *platform, NMPObjectType obj_type, int ifindex)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_object(&lookup, obj_type, ifindex);
-    return nm_platform_lookup(platform, &lookup);
-}
-
-static inline GPtrArray *
-nm_platform_lookup_object_clone(NMPlatform *           platform,
-                                NMPObjectType          obj_type,
-                                int                    ifindex,
-                                NMPObjectPredicateFunc predicate,
-                                gpointer               user_data)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_object(&lookup, obj_type, ifindex);
-    return nm_platform_lookup_clone(platform, &lookup, predicate, user_data);
-}
-
-static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_route_default(NMPlatform *platform, NMPObjectType obj_type)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_route_default(&lookup, obj_type);
-    return nm_platform_lookup(platform, &lookup);
-}
-
-static inline GPtrArray *
-nm_platform_lookup_route_default_clone(NMPlatform *           platform,
-                                       NMPObjectType          obj_type,
-                                       NMPObjectPredicateFunc predicate,
-                                       gpointer               user_data)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_route_default(&lookup, obj_type);
-    return nm_platform_lookup_clone(platform, &lookup, predicate, user_data);
-}
-
-static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_ip4_route_by_weak_id(NMPlatform *platform,
-                                        in_addr_t   network,
-                                        guint       plen,
-                                        guint32     metric,
-                                        guint8      tos)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_ip4_route_by_weak_id(&lookup, network, plen, metric, tos);
-    return nm_platform_lookup(platform, &lookup);
-}
-
-static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_ip6_route_by_weak_id(NMPlatform *           platform,
-                                        const struct in6_addr *network,
-                                        guint                  plen,
-                                        guint32                metric,
-                                        const struct in6_addr *src,
-                                        guint8                 src_plen)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_ip6_route_by_weak_id(&lookup, network, plen, metric, src, src_plen);
-    return nm_platform_lookup(platform, &lookup);
-}
-
-static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_object_by_addr_family(NMPlatform *  platform,
-                                         NMPObjectType obj_type,
-                                         int           addr_family)
-{
-    NMPLookup lookup;
-
-    nmp_lookup_init_object_by_addr_family(&lookup, obj_type, addr_family);
-    return nm_platform_lookup(platform, &lookup);
-}
-
-/*****************************************************************************/
-
-static inline const char *
-nmp_object_link_get_ifname(const NMPObject *obj)
-{
-    if (!obj)
-        return NULL;
-    return NMP_OBJECT_CAST_LINK(obj)->name;
-}
-
-static inline gboolean
-nmp_object_ip_route_is_best_defaut_route(const NMPObject *obj)
-{
-    const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE(obj);
-
-    /* return whether @obj is considered a default-route.
-     *
-     * NMIP4Config/NMIP6Config tracks the (best) default-route explicitly, because
-     * at various places we act differently depending on whether there is a default-route
-     * configured.
-     *
-     * Note that this only considers the main routing table. */
-    return r && NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r)
-           && nm_platform_route_table_is_main(r->table_coerced)
-           && r->type_coerced == nm_platform_route_type_coerce(1 /* RTN_UNICAST */);
-}
-
-static inline gboolean
-nmp_object_ip6_address_is_not_link_local(const NMPObject *obj)
-{
-    return !IN6_IS_ADDR_LINKLOCAL(&NMP_OBJECT_CAST_IP6_ADDRESS(obj)->address);
-}
-
-/*****************************************************************************/
-
-static inline gboolean
-nm_platform_dedup_multi_iter_next_obj(NMDedupMultiIter *ipconf_iter,
-                                      const NMPObject **out_obj,
-                                      NMPObjectType     assert_obj_type)
-{
-    gboolean has_next;
-
-    has_next = nm_dedup_multi_iter_next(ipconf_iter);
-    nm_assert(assert_obj_type == NMP_OBJECT_TYPE_UNKNOWN || !has_next
-              || NMP_OBJECT_GET_TYPE(ipconf_iter->current->obj) == assert_obj_type);
-    NM_SET_OUT(out_obj, has_next ? ipconf_iter->current->obj : NULL);
-    return has_next;
-}
-
-#define _nm_platform_dedup_multi_iter_next(ipconf_iter, out_obj, field, ...)                  \
-    ({                                                                                        \
-        NMDedupMultiIter *const                           _ipconf_iter = (ipconf_iter);       \
-        const typeof(((NMPObject *) NULL)->field) **const _out_obj     = (out_obj);           \
-        gboolean                                          _has_next;                          \
-                                                                                              \
-        if (G_LIKELY(nm_dedup_multi_iter_next(_ipconf_iter))) {                               \
-            if (_out_obj) {                                                                   \
-                *_out_obj = _NMP_OBJECT_CAST(_ipconf_iter->current->obj, field, __VA_ARGS__); \
-            } else {                                                                          \
-                nm_assert(                                                                    \
-                    NM_IN_SET(NMP_OBJECT_GET_TYPE(_ipconf_iter->current->obj), __VA_ARGS__)); \
-            }                                                                                 \
-            _has_next = TRUE;                                                                 \
-        } else {                                                                              \
-            if (_out_obj)                                                                     \
-                *_out_obj = NULL;                                                             \
-            _has_next = FALSE;                                                                \
-        }                                                                                     \
-        _has_next;                                                                            \
-    })
-
-#define nm_platform_dedup_multi_iter_next_ip_address(ipconf_iter, out_obj) \
-    _nm_platform_dedup_multi_iter_next((ipconf_iter),                      \
-                                       (out_obj),                          \
-                                       ip_address,                         \
-                                       NMP_OBJECT_TYPE_IP4_ADDRESS,        \
-                                       NMP_OBJECT_TYPE_IP6_ADDRESS)
-
-#define nm_platform_dedup_multi_iter_next_ip4_address(ipconf_iter, out_obj) \
-    _nm_platform_dedup_multi_iter_next((ipconf_iter),                       \
-                                       (out_obj),                           \
-                                       ip4_address,                         \
-                                       NMP_OBJECT_TYPE_IP4_ADDRESS)
-
-#define nm_platform_dedup_multi_iter_next_ip6_address(ipconf_iter, out_obj) \
-    _nm_platform_dedup_multi_iter_next((ipconf_iter),                       \
-                                       (out_obj),                           \
-                                       ip6_address,                         \
-                                       NMP_OBJECT_TYPE_IP6_ADDRESS)
-
-#define nm_platform_dedup_multi_iter_next_ip_route(ipconf_iter, out_obj) \
-    _nm_platform_dedup_multi_iter_next((ipconf_iter),                    \
-                                       (out_obj),                        \
-                                       ip_route,                         \
-                                       NMP_OBJECT_TYPE_IP4_ROUTE,        \
-                                       NMP_OBJECT_TYPE_IP6_ROUTE)
-
-#define nm_platform_dedup_multi_iter_next_ip4_route(ipconf_iter, out_obj) \
-    _nm_platform_dedup_multi_iter_next((ipconf_iter),                     \
-                                       (out_obj),                         \
-                                       ip4_route,                         \
-                                       NMP_OBJECT_TYPE_IP4_ROUTE)
-
-#define nm_platform_dedup_multi_iter_next_ip6_route(ipconf_iter, out_obj) \
-    _nm_platform_dedup_multi_iter_next((ipconf_iter),                     \
-                                       (out_obj),                         \
-                                       ip6_route,                         \
-                                       NMP_OBJECT_TYPE_IP6_ROUTE)
-
-#endif /* __NMP_OBJECT_H__ */
diff --git a/src/core/platform/nmp-rules-manager.c b/src/core/platform/nmp-rules-manager.c
deleted file mode 100644
index 0c617b80..00000000
--- a/src/core/platform/nmp-rules-manager.c
+++ /dev/null
@@ -1,808 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nmp-rules-manager.h"
-
-#include <linux/fib_rules.h>
-#include <linux/rtnetlink.h>
-
-#include "nm-std-aux/c-list-util.h"
-#include "nmp-object.h"
-
-/*****************************************************************************/
-
-struct _NMPRulesManager {
-    NMPlatform *platform;
-    GHashTable *by_obj;
-    GHashTable *by_user_tag;
-    GHashTable *by_data;
-    guint       ref_count;
-};
-
-/*****************************************************************************/
-
-static void _rules_init(NMPRulesManager *self);
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN      LOGD_PLATFORM
-#define _NMLOG_PREFIX_NAME "rules-manager"
-
-#define _NMLOG(level, ...)                                                 \
-    G_STMT_START                                                           \
-    {                                                                      \
-        const NMLogLevel __level = (level);                                \
-                                                                           \
-        if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) {                  \
-            _nm_log(__level,                                               \
-                    _NMLOG_DOMAIN,                                         \
-                    0,                                                     \
-                    NULL,                                                  \
-                    NULL,                                                  \
-                    "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),             \
-                    _NMLOG_PREFIX_NAME _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
-        }                                                                  \
-    }                                                                      \
-    G_STMT_END
-
-/*****************************************************************************/
-
-static gboolean
-NMP_IS_RULES_MANAGER(gpointer self)
-{
-    return self && ((NMPRulesManager *) self)->ref_count > 0
-           && NM_IS_PLATFORM(((NMPRulesManager *) self)->platform);
-}
-
-#define _USER_TAG_LOG(user_tag) nm_hash_obfuscate_ptr(1240261787u, (user_tag))
-
-/*****************************************************************************/
-
-typedef struct {
-    const NMPObject *obj;
-    gconstpointer    user_tag;
-    CList            obj_lst;
-    CList            user_tag_lst;
-
-    /* track_priority_val zero is special: those are weakly tracked rules.
-     * That means: NetworkManager will restore them only if it removed them earlier.
-     * But it will not remove or add them otherwise.
-     *
-     * Otherwise, the track_priority_val goes together with track_priority_present.
-     * In case of one rule being tracked multiple times (with different priorities),
-     * the one with higher priority wins. See _rules_obj_get_best_data().
-     * Then, the winning present state either enforces that the rule is present
-     * or absent.
-     *
-     * If a rules is not tracked at all, it is ignored by NetworkManager. Assuming
-     * that it was added externally by the user. But unlike weakly tracked rules,
-     * NM will *not* restore such rules if NetworkManager themself removed them. */
-    guint32 track_priority_val;
-    bool    track_priority_present : 1;
-
-    bool dirty : 1;
-} RulesData;
-
-typedef enum {
-    CONFIG_STATE_NONE          = 0,
-    CONFIG_STATE_ADDED_BY_US   = 1,
-    CONFIG_STATE_REMOVED_BY_US = 2,
-
-    /* ConfigState encodes whether the rule was touched by us at all (CONFIG_STATE_NONE).
-     *
-     * Maybe we would only need to track whether we touched the rule at all. But we
-     * track it more in detail what we did: did we add it (CONFIG_STATE_ADDED_BY_US)
-     * or did we remove it (CONFIG_STATE_REMOVED_BY_US)?
-     * Finally, we need CONFIG_STATE_OWNED_BY_US, which means that we didn't actively
-     * add/remove it, but whenever we are about to undo the add/remove, we need to do it.
-     * In that sense, CONFIG_STATE_OWNED_BY_US is really just a flag that we unconditionally
-     * force the state next time when necessary. */
-    CONFIG_STATE_OWNED_BY_US = 3,
-} ConfigState;
-
-typedef struct {
-    const NMPObject *obj;
-    CList            obj_lst_head;
-
-    /* indicates whether we configured/removed the rule (during sync()). We need that, so
-     * if the rule gets untracked, that we know to remove/restore it.
-     *
-     * This makes NMPRulesManager stateful (beyond the configuration that indicates
-     * which rules are tracked).
-     * After a restart, NetworkManager would no longer remember which rules were added
-     * by us.
-     *
-     * That is partially fixed by NetworkManager taking over the rules that it
-     * actively configures (see %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG). */
-    ConfigState config_state;
-} RulesObjData;
-
-typedef struct {
-    gconstpointer user_tag;
-    CList         user_tag_lst_head;
-} RulesUserTagData;
-
-/*****************************************************************************/
-
-static void _rules_data_untrack(NMPRulesManager *self,
-                                RulesData *      rules_data,
-                                gboolean         remove_user_tag_data,
-                                gboolean         make_owned_by_us);
-
-/*****************************************************************************/
-
-static void
-_rules_data_assert(const RulesData *rules_data, gboolean linked)
-{
-    nm_assert(rules_data);
-    nm_assert(NMP_OBJECT_GET_TYPE(rules_data->obj) == NMP_OBJECT_TYPE_ROUTING_RULE);
-    nm_assert(nmp_object_is_visible(rules_data->obj));
-    nm_assert(rules_data->user_tag);
-    nm_assert(!linked || !c_list_is_empty(&rules_data->obj_lst));
-    nm_assert(!linked || !c_list_is_empty(&rules_data->user_tag_lst));
-}
-
-static guint
-_rules_data_hash(gconstpointer data)
-{
-    const RulesData *rules_data = data;
-    NMHashState      h;
-
-    _rules_data_assert(rules_data, FALSE);
-
-    nm_hash_init(&h, 269297543u);
-    nm_platform_routing_rule_hash_update(NMP_OBJECT_CAST_ROUTING_RULE(rules_data->obj),
-                                         NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID,
-                                         &h);
-    nm_hash_update_val(&h, rules_data->user_tag);
-    return nm_hash_complete(&h);
-}
-
-static gboolean
-_rules_data_equal(gconstpointer data_a, gconstpointer data_b)
-{
-    const RulesData *rules_data_a = data_a;
-    const RulesData *rules_data_b = data_b;
-
-    _rules_data_assert(rules_data_a, FALSE);
-    _rules_data_assert(rules_data_b, FALSE);
-
-    return rules_data_a->user_tag == rules_data_b->user_tag
-           && (nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(rules_data_a->obj),
-                                            NMP_OBJECT_CAST_ROUTING_RULE(rules_data_b->obj),
-                                            NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID)
-               == 0);
-}
-
-static void
-_rules_data_destroy(gpointer data)
-{
-    RulesData *rules_data = data;
-
-    _rules_data_assert(rules_data, FALSE);
-
-    c_list_unlink_stale(&rules_data->obj_lst);
-    c_list_unlink_stale(&rules_data->user_tag_lst);
-    nmp_object_unref(rules_data->obj);
-    g_slice_free(RulesData, rules_data);
-}
-
-static const RulesData *
-_rules_obj_get_best_data(RulesObjData *obj_data)
-{
-    RulesData *      rules_data;
-    const RulesData *rd_best = NULL;
-
-    c_list_for_each_entry (rules_data, &obj_data->obj_lst_head, obj_lst) {
-        _rules_data_assert(rules_data, TRUE);
-
-        if (rd_best) {
-            if (rd_best->track_priority_val > rules_data->track_priority_val)
-                continue;
-            if (rd_best->track_priority_val == rules_data->track_priority_val) {
-                if (rd_best->track_priority_present || !rules_data->track_priority_present) {
-                    /* if the priorities are identical, then "present" wins over
-                     * "!present" (absent). */
-                    continue;
-                }
-            }
-        }
-
-        rd_best = rules_data;
-    }
-
-    return rd_best;
-}
-
-static guint
-_rules_obj_hash(gconstpointer data)
-{
-    const RulesObjData *obj_data = data;
-    NMHashState         h;
-
-    nm_hash_init(&h, 432817559u);
-    nm_platform_routing_rule_hash_update(NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj),
-                                         NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID,
-                                         &h);
-    return nm_hash_complete(&h);
-}
-
-static gboolean
-_rules_obj_equal(gconstpointer data_a, gconstpointer data_b)
-{
-    const RulesObjData *obj_data_a = data_a;
-    const RulesObjData *obj_data_b = data_b;
-
-    return (nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(obj_data_a->obj),
-                                         NMP_OBJECT_CAST_ROUTING_RULE(obj_data_b->obj),
-                                         NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID)
-            == 0);
-}
-
-static void
-_rules_obj_destroy(gpointer data)
-{
-    RulesObjData *obj_data = data;
-
-    c_list_unlink_stale(&obj_data->obj_lst_head);
-    nmp_object_unref(obj_data->obj);
-    g_slice_free(RulesObjData, obj_data);
-}
-
-static guint
-_rules_user_tag_hash(gconstpointer data)
-{
-    const RulesUserTagData *user_tag_data = data;
-
-    return nm_hash_val(644693447u, user_tag_data->user_tag);
-}
-
-static gboolean
-_rules_user_tag_equal(gconstpointer data_a, gconstpointer data_b)
-{
-    const RulesUserTagData *user_tag_data_a = data_a;
-    const RulesUserTagData *user_tag_data_b = data_b;
-
-    return user_tag_data_a->user_tag == user_tag_data_b->user_tag;
-}
-
-static void
-_rules_user_tag_destroy(gpointer data)
-{
-    RulesUserTagData *user_tag_data = data;
-
-    c_list_unlink_stale(&user_tag_data->user_tag_lst_head);
-    g_slice_free(RulesUserTagData, user_tag_data);
-}
-
-static RulesData *
-_rules_data_lookup(GHashTable *by_data, const NMPObject *obj, gconstpointer user_tag)
-{
-    RulesData rules_data_needle = {
-        .obj      = obj,
-        .user_tag = user_tag,
-    };
-
-    return g_hash_table_lookup(by_data, &rules_data_needle);
-}
-
-/**
- * nmp_rules_manager_track:
- * @self: the #NMPRulesManager instance
- * @routing_rule: the #NMPlatformRoutingRule to track or untrack
- * @track_priority: the priority for tracking the rule. Note that
- *   negative values indicate a forced absence of the rule. Priorities
- *   are compared with their absolute values (with higher absolute
- *   value being more important). For example, if you track the same
- *   rule twice, once with priority -5 and +10, then the rule is
- *   present (because the positive number is more important).
- *   The special value 0 indicates weakly-tracked rules.
- * @user_tag: the tag associated with tracking this rule. The same tag
- *   must be used to untrack the rule later.
- * @user_tag_untrack: if not %NULL, at the same time untrack this user-tag
- *   for the same rule. Note that this is different from a plain nmp_rules_manager_untrack(),
- *   because it enforces ownership of the now tracked rule. On the other hand,
- *   a plain nmp_rules_manager_untrack() merely forgets about the tracking.
- *   The purpose here is to set this to %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG.
- */
-void
-nmp_rules_manager_track(NMPRulesManager *            self,
-                        const NMPlatformRoutingRule *routing_rule,
-                        gint32                       track_priority,
-                        gconstpointer                user_tag,
-                        gconstpointer                user_tag_untrack)
-{
-    NMPObject         obj_stack;
-    const NMPObject * p_obj_stack;
-    RulesData *       rules_data;
-    RulesObjData *    obj_data;
-    RulesUserTagData *user_tag_data;
-    gboolean          changed = FALSE;
-    guint32           track_priority_val;
-    gboolean          track_priority_present;
-
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-    g_return_if_fail(routing_rule);
-    g_return_if_fail(user_tag);
-    nm_assert(track_priority != G_MININT32);
-
-    _rules_init(self);
-
-    p_obj_stack = nmp_object_stackinit(&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule);
-
-    nm_assert(nmp_object_is_visible(p_obj_stack));
-
-    if (track_priority >= 0) {
-        track_priority_val     = track_priority;
-        track_priority_present = TRUE;
-    } else {
-        track_priority_val     = -track_priority;
-        track_priority_present = FALSE;
-    }
-
-    rules_data = _rules_data_lookup(self->by_data, p_obj_stack, user_tag);
-
-    if (!rules_data) {
-        rules_data  = g_slice_new(RulesData);
-        *rules_data = (RulesData){
-            .obj      = nm_dedup_multi_index_obj_intern(nm_platform_get_multi_idx(self->platform),
-                                                   p_obj_stack),
-            .user_tag = user_tag,
-            .track_priority_val     = track_priority_val,
-            .track_priority_present = track_priority_present,
-            .dirty                  = FALSE,
-        };
-        g_hash_table_add(self->by_data, rules_data);
-
-        obj_data = g_hash_table_lookup(self->by_obj, &rules_data->obj);
-        if (!obj_data) {
-            obj_data  = g_slice_new(RulesObjData);
-            *obj_data = (RulesObjData){
-                .obj          = nmp_object_ref(rules_data->obj),
-                .obj_lst_head = C_LIST_INIT(obj_data->obj_lst_head),
-                .config_state = CONFIG_STATE_NONE,
-            };
-            g_hash_table_add(self->by_obj, obj_data);
-        }
-        c_list_link_tail(&obj_data->obj_lst_head, &rules_data->obj_lst);
-
-        user_tag_data = g_hash_table_lookup(self->by_user_tag, &rules_data->user_tag);
-        if (!user_tag_data) {
-            user_tag_data  = g_slice_new(RulesUserTagData);
-            *user_tag_data = (RulesUserTagData){
-                .user_tag          = user_tag,
-                .user_tag_lst_head = C_LIST_INIT(user_tag_data->user_tag_lst_head),
-            };
-            g_hash_table_add(self->by_user_tag, user_tag_data);
-        }
-        c_list_link_tail(&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst);
-        changed = TRUE;
-    } else {
-        rules_data->dirty = FALSE;
-        if (rules_data->track_priority_val != track_priority_val
-            || rules_data->track_priority_present != track_priority_present) {
-            rules_data->track_priority_val     = track_priority_val;
-            rules_data->track_priority_present = track_priority_present;
-            changed                            = TRUE;
-        }
-    }
-
-    if (user_tag_untrack) {
-        if (user_tag != user_tag_untrack) {
-            RulesData *rules_data_untrack;
-
-            rules_data_untrack = _rules_data_lookup(self->by_data, p_obj_stack, user_tag_untrack);
-            if (rules_data_untrack)
-                _rules_data_untrack(self, rules_data_untrack, FALSE, TRUE);
-        } else
-            nm_assert_not_reached();
-    }
-
-    _rules_data_assert(rules_data, TRUE);
-
-    if (changed) {
-        _LOGD("routing-rule: track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] \"%s\")",
-              _USER_TAG_LOG(rules_data->user_tag),
-              (rules_data->track_priority_val == 0
-                   ? ""
-                   : (rules_data->track_priority_present ? "+" : "-")),
-              (guint) rules_data->track_priority_val,
-              nmp_object_to_string(rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-    }
-}
-
-static void
-_rules_data_untrack(NMPRulesManager *self,
-                    RulesData *      rules_data,
-                    gboolean         remove_user_tag_data,
-                    gboolean         make_owned_by_us)
-{
-    RulesObjData *obj_data;
-
-    nm_assert(NMP_IS_RULES_MANAGER(self));
-    _rules_data_assert(rules_data, TRUE);
-    nm_assert(self->by_data);
-    nm_assert(g_hash_table_lookup(self->by_data, rules_data) == rules_data);
-
-    _LOGD("routing-rule: untrack [" NM_HASH_OBFUSCATE_PTR_FMT "] \"%s\"",
-          _USER_TAG_LOG(rules_data->user_tag),
-          nmp_object_to_string(rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-
-#if NM_MORE_ASSERTS
-    {
-        RulesUserTagData *user_tag_data;
-
-        user_tag_data = g_hash_table_lookup(self->by_user_tag, &rules_data->user_tag);
-        nm_assert(user_tag_data);
-        nm_assert(c_list_contains(&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst));
-    }
-#endif
-
-    nm_assert(!c_list_is_empty(&rules_data->user_tag_lst));
-
-    obj_data = g_hash_table_lookup(self->by_obj, &rules_data->obj);
-    nm_assert(obj_data);
-    nm_assert(c_list_contains(&obj_data->obj_lst_head, &rules_data->obj_lst));
-    nm_assert(obj_data == g_hash_table_lookup(self->by_obj, &rules_data->obj));
-
-    if (make_owned_by_us) {
-        if (obj_data->config_state == CONFIG_STATE_NONE) {
-            /* we need to mark this entry that it requires a touch on the next
-             * sync. */
-            obj_data->config_state = CONFIG_STATE_OWNED_BY_US;
-        }
-    } else if (remove_user_tag_data && c_list_length_is(&rules_data->user_tag_lst, 1))
-        g_hash_table_remove(self->by_user_tag, &rules_data->user_tag);
-
-    /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry
-     * around for the next sync -- so that we can undo what we did earlier. */
-    if (obj_data->config_state == CONFIG_STATE_NONE && c_list_length_is(&rules_data->obj_lst, 1))
-        g_hash_table_remove(self->by_obj, &rules_data->obj);
-
-    g_hash_table_remove(self->by_data, rules_data);
-}
-
-void
-nmp_rules_manager_untrack(NMPRulesManager *            self,
-                          const NMPlatformRoutingRule *routing_rule,
-                          gconstpointer                user_tag)
-{
-    NMPObject        obj_stack;
-    const NMPObject *p_obj_stack;
-    RulesData *      rules_data;
-
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-    g_return_if_fail(routing_rule);
-    g_return_if_fail(user_tag);
-
-    _rules_init(self);
-
-    p_obj_stack = nmp_object_stackinit(&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule);
-
-    nm_assert(nmp_object_is_visible(p_obj_stack));
-
-    rules_data = _rules_data_lookup(self->by_data, p_obj_stack, user_tag);
-    if (rules_data)
-        _rules_data_untrack(self, rules_data, TRUE, FALSE);
-}
-
-void
-nmp_rules_manager_set_dirty(NMPRulesManager *self, gconstpointer user_tag)
-{
-    RulesData *       rules_data;
-    RulesUserTagData *user_tag_data;
-
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-    g_return_if_fail(user_tag);
-
-    if (!self->by_data)
-        return;
-
-    user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag);
-    if (!user_tag_data)
-        return;
-
-    c_list_for_each_entry (rules_data, &user_tag_data->user_tag_lst_head, user_tag_lst)
-        rules_data->dirty = TRUE;
-}
-
-void
-nmp_rules_manager_untrack_all(NMPRulesManager *self,
-                              gconstpointer    user_tag,
-                              gboolean         all /* or only dirty */)
-{
-    RulesData *       rules_data;
-    RulesData *       rules_data_safe;
-    RulesUserTagData *user_tag_data;
-
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-    g_return_if_fail(user_tag);
-
-    if (!self->by_data)
-        return;
-
-    user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag);
-    if (!user_tag_data)
-        return;
-
-    c_list_for_each_entry_safe (rules_data,
-                                rules_data_safe,
-                                &user_tag_data->user_tag_lst_head,
-                                user_tag_lst) {
-        if (all || rules_data->dirty)
-            _rules_data_untrack(self, rules_data, FALSE, FALSE);
-    }
-    if (c_list_is_empty(&user_tag_data->user_tag_lst_head))
-        g_hash_table_remove(self->by_user_tag, user_tag_data);
-}
-
-void
-nmp_rules_manager_sync(NMPRulesManager *self, gboolean keep_deleted_rules)
-{
-    const NMDedupMultiHeadEntry *pl_head_entry;
-    NMDedupMultiIter             pl_iter;
-    const NMPObject *            plobj;
-    gs_unref_ptrarray GPtrArray *rules_to_delete = NULL;
-    RulesObjData *               obj_data;
-    GHashTableIter               h_iter;
-    guint                        i;
-    const RulesData *            rd_best;
-
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-
-    if (!self->by_data)
-        return;
-
-    _LOGD("sync%s", keep_deleted_rules ? " (don't remove any rules)" : "");
-
-    pl_head_entry = nm_platform_lookup_obj_type(self->platform, NMP_OBJECT_TYPE_ROUTING_RULE);
-    if (pl_head_entry) {
-        nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) {
-            obj_data = g_hash_table_lookup(self->by_obj, &plobj);
-
-            if (!obj_data) {
-                /* this rule is not tracked. It was externally added, hence we
-                 * ignore it. */
-                continue;
-            }
-
-            rd_best = _rules_obj_get_best_data(obj_data);
-            if (rd_best) {
-                if (rd_best->track_priority_present) {
-                    if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US)
-                        obj_data->config_state = CONFIG_STATE_ADDED_BY_US;
-                    continue;
-                }
-                if (rd_best->track_priority_val == 0) {
-                    if (!NM_IN_SET(obj_data->config_state,
-                                   CONFIG_STATE_ADDED_BY_US,
-                                   CONFIG_STATE_OWNED_BY_US)) {
-                        obj_data->config_state = CONFIG_STATE_NONE;
-                        continue;
-                    }
-                    obj_data->config_state = CONFIG_STATE_NONE;
-                }
-            }
-
-            if (keep_deleted_rules) {
-                _LOGD("forget/leak rule added by us: %s",
-                      nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-                continue;
-            }
-
-            if (!rules_to_delete)
-                rules_to_delete = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
-
-            g_ptr_array_add(rules_to_delete, (gpointer) nmp_object_ref(plobj));
-
-            obj_data->config_state = CONFIG_STATE_REMOVED_BY_US;
-        }
-    }
-
-    if (rules_to_delete) {
-        for (i = 0; i < rules_to_delete->len; i++)
-            nm_platform_object_delete(self->platform, rules_to_delete->pdata[i]);
-    }
-
-    g_hash_table_iter_init(&h_iter, self->by_obj);
-    while (g_hash_table_iter_next(&h_iter, (gpointer *) &obj_data, NULL)) {
-        rd_best = _rules_obj_get_best_data(obj_data);
-
-        if (!rd_best) {
-            g_hash_table_iter_remove(&h_iter);
-            continue;
-        }
-
-        if (!rd_best->track_priority_present) {
-            if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US)
-                obj_data->config_state = CONFIG_STATE_REMOVED_BY_US;
-            continue;
-        }
-        if (rd_best->track_priority_val == 0) {
-            if (!NM_IN_SET(obj_data->config_state,
-                           CONFIG_STATE_REMOVED_BY_US,
-                           CONFIG_STATE_OWNED_BY_US)) {
-                obj_data->config_state = CONFIG_STATE_NONE;
-                continue;
-            }
-            obj_data->config_state = CONFIG_STATE_NONE;
-        }
-
-        plobj =
-            nm_platform_lookup_obj(self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj);
-        if (plobj)
-            continue;
-
-        obj_data->config_state = CONFIG_STATE_ADDED_BY_US;
-        nm_platform_routing_rule_add(self->platform,
-                                     NMP_NLM_FLAG_ADD,
-                                     NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj));
-    }
-}
-
-void
-nmp_rules_manager_track_from_platform(NMPRulesManager *self,
-                                      NMPlatform *     platform,
-                                      int              addr_family,
-                                      gint32           tracking_priority,
-                                      gconstpointer    user_tag)
-{
-    NMPLookup                    lookup;
-    const NMDedupMultiHeadEntry *head_entry;
-    NMDedupMultiIter             iter;
-    const NMPObject *            o;
-
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-
-    if (!platform)
-        platform = self->platform;
-    else
-        g_return_if_fail(NM_IS_PLATFORM(platform));
-
-    nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
-
-    nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE);
-    head_entry = nm_platform_lookup(platform, &lookup);
-    nmp_cache_iter_for_each (&iter, head_entry, &o) {
-        const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE(o);
-
-        if (addr_family != AF_UNSPEC && rr->addr_family != addr_family)
-            continue;
-
-        nmp_rules_manager_track(self, rr, tracking_priority, user_tag, NULL);
-    }
-}
-
-/*****************************************************************************/
-
-void
-nmp_rules_manager_track_default(NMPRulesManager *self,
-                                int              addr_family,
-                                gint32           track_priority,
-                                gconstpointer    user_tag)
-{
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-
-    nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
-
-    /* track the default rules. See also `man ip-rule`. */
-
-    if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) {
-        nmp_rules_manager_track(self,
-                                &((NMPlatformRoutingRule){
-                                    .addr_family = AF_INET,
-                                    .priority    = 0,
-                                    .table       = RT_TABLE_LOCAL,
-                                    .action      = FR_ACT_TO_TBL,
-                                    .protocol    = RTPROT_KERNEL,
-                                }),
-                                track_priority,
-                                user_tag,
-                                NULL);
-        nmp_rules_manager_track(self,
-                                &((NMPlatformRoutingRule){
-                                    .addr_family = AF_INET,
-                                    .priority    = 32766,
-                                    .table       = RT_TABLE_MAIN,
-                                    .action      = FR_ACT_TO_TBL,
-                                    .protocol    = RTPROT_KERNEL,
-                                }),
-                                track_priority,
-                                user_tag,
-                                NULL);
-        nmp_rules_manager_track(self,
-                                &((NMPlatformRoutingRule){
-                                    .addr_family = AF_INET,
-                                    .priority    = 32767,
-                                    .table       = RT_TABLE_DEFAULT,
-                                    .action      = FR_ACT_TO_TBL,
-                                    .protocol    = RTPROT_KERNEL,
-                                }),
-                                track_priority,
-                                user_tag,
-                                NULL);
-    }
-    if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) {
-        nmp_rules_manager_track(self,
-                                &((NMPlatformRoutingRule){
-                                    .addr_family = AF_INET6,
-                                    .priority    = 0,
-                                    .table       = RT_TABLE_LOCAL,
-                                    .action      = FR_ACT_TO_TBL,
-                                    .protocol    = RTPROT_KERNEL,
-                                }),
-                                track_priority,
-                                user_tag,
-                                NULL);
-        nmp_rules_manager_track(self,
-                                &((NMPlatformRoutingRule){
-                                    .addr_family = AF_INET6,
-                                    .priority    = 32766,
-                                    .table       = RT_TABLE_MAIN,
-                                    .action      = FR_ACT_TO_TBL,
-                                    .protocol    = RTPROT_KERNEL,
-                                }),
-                                track_priority,
-                                user_tag,
-                                NULL);
-    }
-}
-
-static void
-_rules_init(NMPRulesManager *self)
-{
-    if (self->by_data)
-        return;
-
-    self->by_data =
-        g_hash_table_new_full(_rules_data_hash, _rules_data_equal, NULL, _rules_data_destroy);
-    self->by_obj =
-        g_hash_table_new_full(_rules_obj_hash, _rules_obj_equal, NULL, _rules_obj_destroy);
-    self->by_user_tag = g_hash_table_new_full(_rules_user_tag_hash,
-                                              _rules_user_tag_equal,
-                                              NULL,
-                                              _rules_user_tag_destroy);
-}
-
-/*****************************************************************************/
-
-NMPRulesManager *
-nmp_rules_manager_new(NMPlatform *platform)
-{
-    NMPRulesManager *self;
-
-    g_return_val_if_fail(NM_IS_PLATFORM(platform), NULL);
-
-    self  = g_slice_new(NMPRulesManager);
-    *self = (NMPRulesManager){
-        .ref_count = 1,
-        .platform  = g_object_ref(platform),
-    };
-    return self;
-}
-
-void
-nmp_rules_manager_ref(NMPRulesManager *self)
-{
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-
-    self->ref_count++;
-}
-
-void
-nmp_rules_manager_unref(NMPRulesManager *self)
-{
-    g_return_if_fail(NMP_IS_RULES_MANAGER(self));
-
-    if (--self->ref_count > 0)
-        return;
-
-    if (self->by_data) {
-        g_hash_table_destroy(self->by_user_tag);
-        g_hash_table_destroy(self->by_obj);
-        g_hash_table_destroy(self->by_data);
-    }
-    g_object_unref(self->platform);
-    g_slice_free(NMPRulesManager, self);
-}
diff --git a/src/core/platform/nmp-rules-manager.h b/src/core/platform/nmp-rules-manager.h
deleted file mode 100644
index 69cf9075..00000000
--- a/src/core/platform/nmp-rules-manager.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef __NMP_RULES_MANAGER_H__
-#define __NMP_RULES_MANAGER_H__
-
-#include "nm-platform.h"
-
-/*****************************************************************************/
-
-#define NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG ((const void *) nmp_rules_manager_new)
-
-typedef struct _NMPRulesManager NMPRulesManager;
-
-NMPRulesManager *nmp_rules_manager_new(NMPlatform *platform);
-
-void nmp_rules_manager_ref(NMPRulesManager *self);
-void nmp_rules_manager_unref(NMPRulesManager *self);
-
-#define nm_auto_unref_rules_manager nm_auto(_nmp_rules_manager_unref)
-NM_AUTO_DEFINE_FCN0(NMPRulesManager *, _nmp_rules_manager_unref, nmp_rules_manager_unref);
-
-void nmp_rules_manager_track(NMPRulesManager *            self,
-                             const NMPlatformRoutingRule *routing_rule,
-                             gint32                       track_priority,
-                             gconstpointer                user_tag,
-                             gconstpointer                user_tag_untrack);
-
-void nmp_rules_manager_track_default(NMPRulesManager *self,
-                                     int              addr_family,
-                                     gint32           track_priority,
-                                     gconstpointer    user_tag);
-
-void nmp_rules_manager_track_from_platform(NMPRulesManager *self,
-                                           NMPlatform *     platform,
-                                           int              addr_family,
-                                           gint32           tracking_priority,
-                                           gconstpointer    user_tag);
-
-void nmp_rules_manager_untrack(NMPRulesManager *            self,
-                               const NMPlatformRoutingRule *routing_rule,
-                               gconstpointer                user_tag);
-
-void nmp_rules_manager_set_dirty(NMPRulesManager *self, gconstpointer user_tag);
-
-void nmp_rules_manager_untrack_all(NMPRulesManager *self,
-                                   gconstpointer    user_tag,
-                                   gboolean         all /* or only dirty */);
-
-void nmp_rules_manager_sync(NMPRulesManager *self, gboolean keep_deleted_rules);
-
-/*****************************************************************************/
-
-#endif /* __NMP_RULES_MANAGER_H__ */
diff --git a/src/core/platform/tests/monitor.c b/src/core/platform/tests/monitor.c
index abdf2cd5..ff6fae65 100644
--- a/src/core/platform/tests/monitor.c
+++ b/src/core/platform/tests/monitor.c
@@ -8,7 +8,7 @@
 #include <stdlib.h>
 #include <syslog.h>
 
-#include "platform/nm-linux-platform.h"
+#include "libnm-platform/nm-linux-platform.h"
 
 #include "nm-test-utils-core.h"
 
diff --git a/src/core/platform/tests/test-address.c b/src/core/platform/tests/test-address.c
index d4d67105..4e1d1d6e 100644
--- a/src/core/platform/tests/test-address.c
+++ b/src/core/platform/tests/test-address.c
@@ -235,7 +235,7 @@ test_ip4_address_general_2(void)
     /* Looks like addresses are not announced by kernel when the interface
      * is down. Link-local IPv6 address is automatically added.
      */
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, DEVICE_IFINDEX, NULL));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, DEVICE_IFINDEX, IFF_UP, TRUE) >= 0);
 
     /* Add/delete notification */
     nmtstp_ip4_address_add(NULL, EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL);
@@ -325,7 +325,7 @@ test_ip4_address_peer(void)
 
     g_assert(addr != addr_peer);
 
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0);
     accept_signals(address_removed, 0, G_MAXINT);
     accept_signals(address_added, 0, G_MAXINT);
 
@@ -397,7 +397,7 @@ test_ip4_address_peer_zero(void)
     peers[1] = addr_peer;
     peers[2] = 0;
 
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0);
 
     nmtst_rand_perm(NULL, r_peers, peers, sizeof(peers[0]), G_N_ELEMENTS(peers));
     for (i = 0; i < G_N_ELEMENTS(peers); i++) {
diff --git a/src/core/platform/tests/test-cleanup.c b/src/core/platform/tests/test-cleanup.c
index b54eca34..e13bb8c8 100644
--- a/src/core/platform/tests/test-cleanup.c
+++ b/src/core/platform/tests/test-cleanup.c
@@ -44,9 +44,12 @@ test_cleanup_internal(void)
     g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL)));
     accept_signal(link_added);
     free_signal(link_added);
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET,
-                                     nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME),
-                                     NULL));
+    g_assert(
+        nm_platform_link_change_flags(NM_PLATFORM_GET,
+                                      nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME),
+                                      IFF_UP,
+                                      TRUE)
+        >= 0);
     ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME);
     g_assert(ifindex > 0);
 
diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c
index 4a117d59..f9a141bc 100644
--- a/src/core/platform/tests/test-common.c
+++ b/src/core/platform/tests/test-common.c
@@ -1413,6 +1413,44 @@ nmtstp_ip6_address_del(NMPlatform *    platform,
     }                                                                                       \
     G_STMT_END
 
+/* Due to rounding errors with clock_t_to_jiffies()/jiffies_to_clock_t(), kernel cannot
+ * store all requested values. That means, when we try to configure a bridge with
+ * the @requested values, the actually configured settings are slightly off, as
+ * @kernel.
+ *
+ * This function takes @requested and returns it as @dst output. All fields
+ * that might be mangled by kernel (according to @kernel) are adjusted. The
+ * result is almost identical to @requested, but some fields might be adjusted
+ * to their @kernel value. */
+const NMPlatformLnkBridge *
+nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested,
+                                          const NMPlatformLnkBridge *kernel,
+                                          NMPlatformLnkBridge *      dst)
+{
+    if (dst != requested)
+        *dst = *requested;
+
+#define _normalize_field(dst, kernel, field)                                         \
+    G_STMT_START                                                                     \
+    {                                                                                \
+        (dst)->field = nmtstp_normalize_jiffies_time((dst)->field, (kernel)->field); \
+    }                                                                                \
+    G_STMT_END
+
+    _normalize_field(dst, kernel, forward_delay);
+    _normalize_field(dst, kernel, hello_time);
+    _normalize_field(dst, kernel, max_age);
+    _normalize_field(dst, kernel, ageing_time);
+    _normalize_field(dst, kernel, mcast_last_member_interval);
+    _normalize_field(dst, kernel, mcast_membership_interval);
+    _normalize_field(dst, kernel, mcast_querier_interval);
+    _normalize_field(dst, kernel, mcast_query_interval);
+    _normalize_field(dst, kernel, mcast_query_response_interval);
+    _normalize_field(dst, kernel, mcast_startup_query_interval);
+
+    return dst;
+}
+
 const NMPlatformLink *
 nmtstp_link_bridge_add(NMPlatform *               platform,
                        gboolean                   external_command,
@@ -1421,7 +1459,8 @@ nmtstp_link_bridge_add(NMPlatform *               platform,
 {
     const NMPlatformLink *     pllink = NULL;
     const NMPlatformLnkBridge *ll     = NULL;
-    int                        r      = 0;
+    NMPlatformLnkBridge        lnk_normalized;
+    int                        r = 0;
 
     g_assert(nm_utils_ifname_valid_kernel(name, NULL));
 
@@ -1542,6 +1581,8 @@ nmtstp_link_bridge_add(NMPlatform *               platform,
 
     ll = NMP_OBJECT_CAST_LNK_BRIDGE(NMP_OBJECT_UP_CAST(pllink)->_link.netlink.lnk);
 
+    lnk = nmtstp_link_bridge_normalize_jiffies_time(lnk, ll, &lnk_normalized);
+
     g_assert_cmpint(lnk->forward_delay, ==, ll->forward_delay);
     g_assert_cmpint(lnk->hello_time, ==, ll->hello_time);
     g_assert_cmpint(lnk->max_age, ==, ll->max_age);
@@ -2243,9 +2284,9 @@ nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifin
         nmtstp_run_command_check("ip link set %s %s", ifname, up ? "up" : "down");
     } else {
         if (up)
-            g_assert(nm_platform_link_set_up(platform, ifindex, NULL));
+            g_assert(nm_platform_link_change_flags(platform, ifindex, IFF_UP, TRUE) >= 0);
         else
-            g_assert(nm_platform_link_set_down(platform, ifindex));
+            g_assert(nm_platform_link_change_flags(platform, ifindex, IFF_UP, FALSE) >= 0);
     }
 
     /* Let's wait until we get the result */
diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h
index 618645fe..1fb97d24 100644
--- a/src/core/platform/tests/test-common.h
+++ b/src/core/platform/tests/test-common.h
@@ -11,10 +11,10 @@
 #include <linux/if_link.h>
 #include <linux/ip6_tunnel.h>
 
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nmp-object.h"
 #include "platform/nm-fake-platform.h"
-#include "platform/nm-linux-platform.h"
+#include "libnm-platform/nm-linux-platform.h"
 
 #include "nm-test-utils-core.h"
 
@@ -22,6 +22,24 @@
 
 /*****************************************************************************/
 
+#define nmtstp_normalize_jiffies_time(requested_value, kernel_value)                             \
+    ({                                                                                           \
+        typeof(kernel_value)  _kernel_value    = (kernel_value);                                 \
+        typeof(_kernel_value) _requested_value = (requested_value);                              \
+                                                                                                 \
+        /* kernel stores some values (like bridge's forward_delay) in jiffies. When converting
+         * back and forth (clock_t_to_jiffies()/jiffies_to_clock_t()), the value reported back
+         * to user space may have rounding errors (of +/- 1), depending on CONFIG_HZ setting.
+         *
+         * Normalize the requested_value to the kernel_value, if it look as if a rounding
+         * error happens. If the difference is larger than +/- 1, no normalization happens! */   \
+                                                                                                 \
+        ((_requested_value >= (NM_MAX(_kernel_value, 1) - 1))                                    \
+         && (_requested_value <= (NM_MIN(_kernel_value, ~((typeof(_kernel_value)) 0) - 1) + 1))) \
+            ? _kernel_value                                                                      \
+            : _requested_value;                                                                  \
+    })
+
 #define _NMLOG_PREFIX_NAME "platform-test"
 #define _NMLOG_DOMAIN      LOGD_PLATFORM
 #define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, __VA_ARGS__)
@@ -437,6 +455,11 @@ gboolean nmtstp_kernel_support_get(NMPlatformKernelSupportType type);
 void
 nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up);
 
+const NMPlatformLnkBridge *
+nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested,
+                                          const NMPlatformLnkBridge *kernel,
+                                          NMPlatformLnkBridge *      dst);
+
 const NMPlatformLink *nmtstp_link_bridge_add(NMPlatform *               platform,
                                              gboolean                   external_command,
                                              const char *               name,
@@ -519,7 +542,7 @@ _nmtstp_env1_wrapper_setup(const NmtstTestData *test_data)
     g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, -1);
 
     if (GPOINTER_TO_INT(p_ifup))
-        g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, *p_ifindex, NULL));
+        g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, *p_ifindex, IFF_UP, TRUE) >= 0);
 
     nm_platform_process_events(NM_PLATFORM_GET);
 
diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c
index 3aaa3759..f6c3841e 100644
--- a/src/core/platform/tests/test-link.c
+++ b/src/core/platform/tests/test-link.c
@@ -11,11 +11,11 @@
 #include <sys/types.h>
 #include <linux/if_tun.h>
 
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-base/nm-ethtool-base.h"
-#include "platform/nmp-object.h"
-#include "nm-platform/nmp-netns.h"
-#include "nm-platform/nm-platform-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "libnm-base/nm-ethtool-base.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nmp-netns.h"
+#include "libnm-platform/nm-platform-utils.h"
 
 #include "test-common.h"
 #include "nm-test-utils-core.h"
@@ -48,13 +48,15 @@ test_bogus(void)
     g_assert(!nm_platform_link_get_type(NM_PLATFORM_GET, BOGUS_IFINDEX));
     g_assert(!nm_platform_link_get_type_name(NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-    g_assert(!nm_platform_link_set_up(NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
+    g_assert(!(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_UP, TRUE) >= 0));
 
-    g_assert(!nm_platform_link_set_down(NM_PLATFORM_GET, BOGUS_IFINDEX));
+    g_assert(!(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_UP, FALSE) >= 0));
 
-    g_assert(!nm_platform_link_set_arp(NM_PLATFORM_GET, BOGUS_IFINDEX));
+    g_assert(
+        !(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_NOARP, TRUE) >= 0));
 
-    g_assert(!nm_platform_link_set_noarp(NM_PLATFORM_GET, BOGUS_IFINDEX));
+    g_assert(
+        !(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_NOARP, FALSE) >= 0));
 
     g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, BOGUS_IFINDEX));
     g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -147,7 +149,8 @@ software_add(NMLinkType link_type, const char *name)
                                                 NM_PLATFORM_SIGNAL_CHANGED,
                                                 link_callback,
                                                 parent_ifindex);
-            g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, parent_ifindex, NULL));
+            g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, parent_ifindex, IFF_UP, TRUE)
+                     >= 0);
             if (was_up) {
                 /* when NM is running in the background, it will mess with addrgenmode which might cause additional signals. */
                 accept_signals(parent_changed, 0, 1);
@@ -233,7 +236,7 @@ test_slave(int master, int type, SignalData *master_changed)
      * See https://bugzilla.redhat.com/show_bug.cgi?id=910348
      */
     g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex));
-    g_assert(nm_platform_link_set_down(NM_PLATFORM_GET, ifindex));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0);
     g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex));
     ensure_no_signal(link_changed);
 
@@ -263,7 +266,7 @@ test_slave(int master, int type, SignalData *master_changed)
                      &test_link_changed_signal_arg2);
 
     /* Set master up */
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, master, NULL));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, master, IFF_UP, TRUE) >= 0);
     g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, master));
     accept_signals(master_changed, 1, 3);
 
@@ -284,7 +287,7 @@ test_slave(int master, int type, SignalData *master_changed)
     switch (nm_platform_link_get_type(NM_PLATFORM_GET, master)) {
     case NM_LINK_TYPE_BOND:
     case NM_LINK_TYPE_TEAM:
-        g_assert(nm_platform_link_set_down(NM_PLATFORM_GET, ifindex));
+        g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0);
         accept_signal(link_changed);
         accept_signals(master_changed, 0, 3);
         break;
@@ -317,7 +320,7 @@ test_slave(int master, int type, SignalData *master_changed)
     }
 
     /* Set slave up and see if master gets up too */
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0);
     g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex));
     g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, master));
     accept_signals(link_changed, 1, 3);
@@ -439,10 +442,10 @@ test_software(NMLinkType link_type, const char *link_typename)
 
     /* Set ARP/NOARP */
     g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex));
-    g_assert(nm_platform_link_set_noarp(NM_PLATFORM_GET, ifindex));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, TRUE) >= 0);
     g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex));
     accept_signals(link_changed, 1, 2);
-    g_assert(nm_platform_link_set_arp(NM_PLATFORM_GET, ifindex));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, FALSE) >= 0);
     g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex));
     accept_signal(link_changed);
 
@@ -672,21 +675,21 @@ test_internal(void)
     /* Up/connected */
     g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex));
     g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex));
-    g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0);
     g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, ifindex));
     g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex));
     accept_signals(link_changed, 1, 2);
-    g_assert(nm_platform_link_set_down(NM_PLATFORM_GET, ifindex));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0);
     g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex));
     g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex));
     accept_signal(link_changed);
 
     /* arp/noarp */
     g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex));
-    g_assert(nm_platform_link_set_arp(NM_PLATFORM_GET, ifindex));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, FALSE) >= 0);
     g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex));
     accept_signal(link_changed);
-    g_assert(nm_platform_link_set_noarp(NM_PLATFORM_GET, ifindex));
+    g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, TRUE) >= 0);
     g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex));
     accept_signal(link_changed);
 
@@ -1303,7 +1306,7 @@ test_software_detect(gconstpointer user_data)
 
         if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "gre0")) {
             /* Seems that the ip_gre module is not loaded... try to load it. */
-            gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0;
+            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0;
         }
 
         if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) {
@@ -1330,7 +1333,7 @@ test_software_detect(gconstpointer user_data)
 
         if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "gretap0")) {
             /* Seems that the ip_gre module is not loaded... try to load it. */
-            gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0;
+            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0;
         }
 
         if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) {
@@ -1350,7 +1353,7 @@ test_software_detect(gconstpointer user_data)
 
         if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "tunl0")) {
             /* Seems that the ipip module is not loaded... try to load it. */
-            gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ipip", NULL) != 0;
+            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ipip", NULL) != 0;
         }
 
         lnk_ipip.local              = nmtst_inet4_from_string("1.2.3.4");
@@ -1376,7 +1379,7 @@ test_software_detect(gconstpointer user_data)
 
         if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6tnl0")) {
             /* Seems that the ip6_tunnel module is not loaded... try to load it. */
-            gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ip6_tunnel", NULL) != 0;
+            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_tunnel", NULL) != 0;
         }
 
         switch (test_data->test_mode) {
@@ -1418,7 +1421,7 @@ test_software_detect(gconstpointer user_data)
 
         if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) {
             /* Seems that the ip6_tunnel module is not loaded... try to load it. */
-            gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0;
+            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0;
         }
 
         lnk_ip6tnl.local          = *nmtst_inet6_from_string("fd01::42");
@@ -1445,7 +1448,7 @@ test_software_detect(gconstpointer user_data)
 
         if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) {
             /* Seems that the ip6_tunnel module is not loaded... try to load it. */
-            gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0;
+            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0;
         }
 
         lnk_ip6tnl.local          = *nmtst_inet6_from_string("fe80::abcd");
@@ -1526,7 +1529,7 @@ test_software_detect(gconstpointer user_data)
 
         if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "sit0")) {
             /* Seems that the sit module is not loaded... try to load it. */
-            gracefully_skip = nm_utils_modprobe(NULL, TRUE, "sit", NULL) != 0;
+            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "sit", NULL) != 0;
         }
 
         if (!nmtstp_link_sit_add(NULL, ext, DEVICE_NAME, &lnk_sit)) {
@@ -1679,17 +1682,25 @@ test_software_detect(gconstpointer user_data)
         case NM_LINK_TYPE_BRIDGE:
         {
             const NMPlatformLnkBridge *plnk = &lnk->lnk_bridge;
+            NMPlatformLnkBridge        lnk_bridge_norm_stack;
+            const NMPlatformLnkBridge *lnk_bridge_norm;
 
             g_assert(plnk == nm_platform_link_get_lnk_bridge(NM_PLATFORM_GET, ifindex, NULL));
-            g_assert_cmpint(nm_platform_lnk_bridge_cmp(&lnk_bridge, plnk), ==, 0);
-            g_assert_cmpint(plnk->forward_delay, ==, 1560);
-            g_assert_cmpint(plnk->hello_time, ==, 150);
-            g_assert_cmpint(plnk->max_age, ==, 2100);
-            g_assert_cmpint(plnk->ageing_time, ==, 2200);
+
+            lnk_bridge_norm = nmtstp_link_bridge_normalize_jiffies_time(&lnk_bridge,
+                                                                        plnk,
+                                                                        &lnk_bridge_norm_stack);
+
+            g_assert_cmpint(nm_platform_lnk_bridge_cmp(lnk_bridge_norm, plnk), ==, 0);
+
+            g_assert_cmpint(plnk->forward_delay, ==, lnk_bridge_norm->forward_delay);
+            g_assert_cmpint(plnk->hello_time, ==, lnk_bridge_norm->hello_time);
+            g_assert_cmpint(plnk->max_age, ==, lnk_bridge_norm->max_age);
+            g_assert_cmpint(plnk->ageing_time, ==, lnk_bridge_norm->ageing_time);
             g_assert_cmpint(plnk->stp_state, ==, TRUE);
             g_assert_cmpint(plnk->priority, ==, 22);
             g_assert_cmpint(plnk->vlan_protocol, ==, 0x8100);
-            g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge.vlan_stats_enabled);
+            g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge_norm->vlan_stats_enabled);
             g_assert_cmpint(plnk->group_fwd_mask, ==, 8);
             g_assert_cmpint(plnk->mcast_snooping, ==, TRUE);
             g_assert_cmpint(plnk->mcast_router, ==, 1);
@@ -1698,13 +1709,22 @@ test_software_detect(gconstpointer user_data)
             g_assert_cmpint(plnk->mcast_hash_max, ==, 1024);
             g_assert_cmpint(plnk->mcast_last_member_count, ==, 2);
             g_assert_cmpint(plnk->mcast_startup_query_count, ==, 3);
-            g_assert_cmpint(plnk->mcast_last_member_interval, ==, 5000);
-            g_assert_cmpint(plnk->mcast_membership_interval, ==, 25000);
-            g_assert_cmpint(plnk->mcast_querier_interval, ==, 26000);
-            g_assert_cmpint(plnk->mcast_query_interval, ==, 12000);
-            g_assert_cmpint(plnk->mcast_query_response_interval, ==, 5200);
-            g_assert_cmpint(plnk->mcast_startup_query_interval, ==, 3000);
-            g_assert_cmpint(nm_platform_lnk_bridge_cmp(&lnk_bridge, plnk), ==, 0);
+            g_assert_cmpint(plnk->mcast_last_member_interval,
+                            ==,
+                            lnk_bridge_norm->mcast_last_member_interval);
+            g_assert_cmpint(plnk->mcast_membership_interval,
+                            ==,
+                            lnk_bridge_norm->mcast_membership_interval);
+            g_assert_cmpint(plnk->mcast_querier_interval,
+                            ==,
+                            lnk_bridge_norm->mcast_querier_interval);
+            g_assert_cmpint(plnk->mcast_query_interval, ==, lnk_bridge_norm->mcast_query_interval);
+            g_assert_cmpint(plnk->mcast_query_response_interval,
+                            ==,
+                            lnk_bridge_norm->mcast_query_response_interval);
+            g_assert_cmpint(plnk->mcast_startup_query_interval,
+                            ==,
+                            lnk_bridge_norm->mcast_startup_query_interval);
             break;
         }
         case NM_LINK_TYPE_GRE:
@@ -2049,7 +2069,7 @@ _assert_xgress_qos_mappings_impl(int ifindex, gboolean is_ingress_map, int n_ent
     _assert_xgress_qos_mappings(ifindex, FALSE, n_entries, __VA_ARGS__)
 
 static void
-_assert_vlan_flags(int ifindex, NMVlanFlags flags)
+_assert_vlan_flags(int ifindex, _NMVlanFlags flags)
 {
     const NMPlatformLnkVlan *plnk;
 
@@ -2362,8 +2382,8 @@ test_vlan_set_xgress(void)
 
         g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET,
                                               ifindex,
-                                              NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP,
-                                              NM_VLAN_FLAG_REORDER_HEADERS,
+                                              _NM_VLAN_FLAG_REORDER_HEADERS | _NM_VLAN_FLAG_GVRP,
+                                              _NM_VLAN_FLAG_REORDER_HEADERS,
                                               TRUE,
                                               ingress_map,
                                               G_N_ELEMENTS(ingress_map),
@@ -2372,7 +2392,7 @@ test_vlan_set_xgress(void)
                                               G_N_ELEMENTS(egress_map)));
         _assert_ingress_qos_mappings(ifindex, 2, 4, 1, 6, 12);
         _assert_egress_qos_mappings(ifindex, 2, 1, 5, 3232, 7);
-        _assert_vlan_flags(ifindex, NM_VLAN_FLAG_REORDER_HEADERS);
+        _assert_vlan_flags(ifindex, _NM_VLAN_FLAG_REORDER_HEADERS);
     }
 
     {
@@ -2393,8 +2413,8 @@ test_vlan_set_xgress(void)
 
         g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET,
                                               ifindex,
-                                              NM_VLAN_FLAG_GVRP,
-                                              NM_VLAN_FLAG_GVRP,
+                                              _NM_VLAN_FLAG_GVRP,
+                                              _NM_VLAN_FLAG_GVRP,
                                               FALSE,
                                               ingress_map,
                                               G_N_ELEMENTS(ingress_map),
@@ -2403,7 +2423,7 @@ test_vlan_set_xgress(void)
                                               G_N_ELEMENTS(egress_map)));
         _assert_ingress_qos_mappings(ifindex, 2, 4, 1, 6, 12);
         _assert_egress_qos_mappings(ifindex, 2, 1, 7, 64, 4);
-        _assert_vlan_flags(ifindex, NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP);
+        _assert_vlan_flags(ifindex, _NM_VLAN_FLAG_REORDER_HEADERS | _NM_VLAN_FLAG_GVRP);
     }
 
     nmtstp_link_delete(NULL, -1, ifindex, DEVICE_NAME, TRUE);
diff --git a/src/core/platform/tests/test-nmp-object.c b/src/core/platform/tests/test-nmp-object.c
index 310611ee..6d2236ec 100644
--- a/src/core/platform/tests/test-nmp-object.c
+++ b/src/core/platform/tests/test-nmp-object.c
@@ -8,8 +8,8 @@
 #include <libudev.h>
 #include <linux/pkt_sched.h>
 
-#include "platform/nmp-object.h"
-#include "nm-udev-aux/nm-udev-utils.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-udev-aux/nm-udev-utils.h"
 
 #include "nm-test-utils-core.h"
 
diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c
index b426cc65..05158228 100644
--- a/src/core/platform/tests/test-platform-general.c
+++ b/src/core/platform/tests/test-platform-general.c
@@ -7,8 +7,8 @@
 
 #include <linux/rtnetlink.h>
 
-#include "nm-platform/nm-platform-utils.h"
-#include "platform/nm-linux-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
+#include "libnm-platform/nm-linux-platform.h"
 
 #include "nm-test-utils-core.h"
 
@@ -584,7 +584,7 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
     nmtst_stable_rand(710086081, rand_map, sizeof(rand_map[0]) * N_ADDRESSES);
 
     for (i = 0; i < N_ADDRESSES; i++) {
-        NMPlatformIPXAddress *a = (gpointer)(&addresses[i * ELM_SIZE]);
+        NMPlatformIPXAddress *a = (gpointer) (&addresses[i * ELM_SIZE]);
         guint64               r = rand_map[i];
         struct in6_addr *     a6;
 
@@ -684,10 +684,10 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                       (gpointer) test_data);
 
     for (i = 0; i < N_ADDRESSES; i++) {
-        const NMPlatformIPXAddress *a = (gconstpointer)(&addresses[i * ELM_SIZE]);
+        const NMPlatformIPXAddress *a = (gconstpointer) (&addresses[i * ELM_SIZE]);
 
         for (j = i + 1; j < N_ADDRESSES; j++) {
-            const NMPlatformIPXAddress *b = (gconstpointer)(&addresses[j * ELM_SIZE]);
+            const NMPlatformIPXAddress *b = (gconstpointer) (&addresses[j * ELM_SIZE]);
             int                         c1;
             int                         c2;
 
diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c
index 645bb5b1..2b9b2f8f 100644
--- a/src/core/platform/tests/test-route.c
+++ b/src/core/platform/tests/test-route.c
@@ -9,8 +9,8 @@
 #include <linux/fib_rules.h>
 
 #include "nm-core-utils.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "platform/nmp-rules-manager.h"
+#include "libnm-platform/nm-platform-utils.h"
+#include "libnm-platform/nmp-rules-manager.h"
 
 #include "test-common.h"
 
diff --git a/src/core/platform/tests/test-tc.c b/src/core/platform/tests/test-tc.c
index d15c3d76..cd9536cc 100644
--- a/src/core/platform/tests/test-tc.c
+++ b/src/core/platform/tests/test-tc.c
@@ -5,9 +5,9 @@
 #include <linux/pkt_sched.h>
 
 #include "nm-test-utils-core.h"
-#include "platform/nmp-object.h"
-#include "nm-platform/nmp-netns.h"
-#include "nm-platform/nm-platform-utils.h"
+#include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nmp-netns.h"
+#include "libnm-platform/nm-platform-utils.h"
 #include "test-common.h"
 
 static NMPObject *
diff --git a/src/core/platform/wifi/nm-wifi-utils-nl80211.c b/src/core/platform/wifi/nm-wifi-utils-nl80211.c
deleted file mode 100644
index ad067248..00000000
--- a/src/core/platform/wifi/nm-wifi-utils-nl80211.c
+++ /dev/null
@@ -1,909 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2005 - 2018 Red Hat, Inc.
- * Copyright (C) 2006 - 2008 Novell, Inc.
- * Copyright (C) 2011 Intel Corporation. All rights reserved.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-wifi-utils-nl80211.h"
-
-#include <sys/ioctl.h>
-#include <net/ethernet.h>
-#include <unistd.h>
-#include <linux/nl80211.h>
-#include <linux/if.h>
-
-#include "nm-platform/nm-netlink.h"
-#include "nm-wifi-utils-private.h"
-#include "platform/nm-platform.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "nm-utils.h"
-
-#define _NMLOG_PREFIX_NAME "wifi-nl80211"
-#define _NMLOG_DOMAIN      LOGD_PLATFORM | LOGD_WIFI
-#define _NMLOG(level, ...)                                                             \
-    G_STMT_START                                                                       \
-    {                                                                                  \
-        char        _ifname_buf[IFNAMSIZ];                                             \
-        const char *_ifname =                                                          \
-            self ? nmp_utils_if_indextoname(self->parent.ifindex, _ifname_buf) : NULL; \
-                                                                                       \
-        nm_log((level),                                                                \
-               _NMLOG_DOMAIN,                                                          \
-               _ifname ?: NULL,                                                        \
-               NULL,                                                                   \
-               "%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                        \
-               _NMLOG_PREFIX_NAME,                                                     \
-               NM_PRINT_FMT_QUOTED(_ifname, " (", _ifname, ")", "")                    \
-                   _NM_UTILS_MACRO_REST(__VA_ARGS__));                                 \
-    }                                                                                  \
-    G_STMT_END
-
-typedef struct {
-    NMWifiUtils     parent;
-    struct nl_sock *nl_sock;
-    guint32 *       freqs;
-    int             id;
-    int             num_freqs;
-    int             phy;
-    bool            can_wowlan : 1;
-} NMWifiUtilsNl80211;
-
-typedef struct {
-    NMWifiUtilsClass parent;
-} NMWifiUtilsNl80211Class;
-
-G_DEFINE_TYPE(NMWifiUtilsNl80211, nm_wifi_utils_nl80211, NM_TYPE_WIFI_UTILS)
-
-static int
-ack_handler(struct nl_msg *msg, void *arg)
-{
-    int *done = arg;
-    *done     = 1;
-    return NL_STOP;
-}
-
-static int
-finish_handler(struct nl_msg *msg, void *arg)
-{
-    int *done = arg;
-    *done     = 1;
-    return NL_SKIP;
-}
-
-static int
-error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
-{
-    int *done = arg;
-    *done     = err->error;
-    return NL_SKIP;
-}
-
-static struct nl_msg *
-_nl80211_alloc_msg(int id, int ifindex, int phy, guint32 cmd, guint32 flags)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-
-    msg = nlmsg_alloc();
-    genlmsg_put(msg, 0, 0, id, 0, flags, cmd, 0);
-    NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-    if (phy != -1)
-        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy);
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static struct nl_msg *
-nl80211_alloc_msg(NMWifiUtilsNl80211 *self, guint32 cmd, guint32 flags)
-{
-    return _nl80211_alloc_msg(self->id, self->parent.ifindex, self->phy, cmd, flags);
-}
-
-static int
-nl80211_send_and_recv(NMWifiUtilsNl80211 *self,
-                      struct nl_msg *     msg,
-                      int (*valid_handler)(struct nl_msg *, void *),
-                      void *valid_data)
-{
-    int                err;
-    int                done = 0;
-    const struct nl_cb cb   = {
-        .err_cb     = error_handler,
-        .err_arg    = &done,
-        .finish_cb  = finish_handler,
-        .finish_arg = &done,
-        .ack_cb     = ack_handler,
-        .ack_arg    = &done,
-        .valid_cb   = valid_handler,
-        .valid_arg  = valid_data,
-    };
-
-    g_return_val_if_fail(msg != NULL, -ENOMEM);
-
-    err = nl_send_auto(self->nl_sock, msg);
-    if (err < 0)
-        return err;
-
-    /* Loop until one of our NL callbacks says we're done; on success
-     * done will be 1, on error it will be < 0.
-     */
-    while (!done) {
-        err = nl_recvmsgs(self->nl_sock, &cb);
-        if (err < 0 && err != -EAGAIN) {
-            /* Kernel scan list can change while we are dumping it, as new scan
-             * results from H/W can arrive. BSS info is assured to be consistent
-             * and we don't need consistent view of whole scan list. Hence do
-             * not warn on DUMP_INTR error for get scan command.
-             */
-            if (err == -NME_NL_DUMP_INTR
-                && genlmsg_hdr(nlmsg_hdr(msg))->cmd == NL80211_CMD_GET_SCAN)
-                break;
-
-            _LOGW("nl_recvmsgs() error: (%d) %s", err, nm_strerror(err));
-            break;
-        }
-    }
-
-    if (err >= 0 && done < 0)
-        err = done;
-    return err;
-}
-
-static void
-dispose(GObject *object)
-{
-    NMWifiUtilsNl80211 *self = NM_WIFI_UTILS_NL80211(object);
-
-    nm_clear_g_free(&self->freqs);
-}
-
-struct nl80211_iface_info {
-    NM80211Mode mode;
-    uint32_t    freq;
-};
-
-static int
-nl80211_iface_info_handler(struct nl_msg *msg, void *arg)
-{
-    struct nl80211_iface_info *info = arg;
-    struct genlmsghdr *        gnlh = nlmsg_data(nlmsg_hdr(msg));
-    struct nlattr *            tb[NL80211_ATTR_MAX + 1];
-
-    if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
-        return NL_SKIP;
-
-    if (!tb[NL80211_ATTR_IFTYPE])
-        return NL_SKIP;
-
-    switch (nla_get_u32(tb[NL80211_ATTR_IFTYPE])) {
-    case NL80211_IFTYPE_ADHOC:
-        info->mode = NM_802_11_MODE_ADHOC;
-        break;
-    case NL80211_IFTYPE_AP:
-        info->mode = NM_802_11_MODE_AP;
-        break;
-    case NL80211_IFTYPE_STATION:
-        info->mode = NM_802_11_MODE_INFRA;
-        break;
-    case NL80211_IFTYPE_MESH_POINT:
-        info->mode = NM_802_11_MODE_MESH;
-        break;
-    }
-
-    if (tb[NL80211_ATTR_WIPHY_FREQ] != NULL)
-        info->freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
-
-    return NL_SKIP;
-}
-
-static NM80211Mode
-wifi_nl80211_get_mode(NMWifiUtils *data)
-{
-    NMWifiUtilsNl80211 *      self       = (NMWifiUtilsNl80211 *) data;
-    struct nl80211_iface_info iface_info = {
-        .mode = NM_802_11_MODE_UNKNOWN,
-    };
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_INTERFACE, 0);
-
-    if (nl80211_send_and_recv(self, msg, nl80211_iface_info_handler, &iface_info) < 0)
-        return NM_802_11_MODE_UNKNOWN;
-
-    return iface_info.mode;
-}
-
-static gboolean
-wifi_nl80211_set_mode(NMWifiUtils *data, const NM80211Mode mode)
-{
-    NMWifiUtilsNl80211 *         self = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg  = NULL;
-    int                          err;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_SET_INTERFACE, 0);
-
-    switch (mode) {
-    case NM_802_11_MODE_INFRA:
-        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
-        break;
-    case NM_802_11_MODE_ADHOC:
-        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_ADHOC);
-        break;
-    case NM_802_11_MODE_AP:
-        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP);
-        break;
-    case NM_802_11_MODE_MESH:
-        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MESH_POINT);
-        break;
-    default:
-        g_assert_not_reached();
-    }
-
-    err = nl80211_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static gboolean
-wifi_nl80211_set_powersave(NMWifiUtils *data, guint32 powersave)
-{
-    NMWifiUtilsNl80211 *         self = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg  = NULL;
-    int                          err;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_SET_POWER_SAVE, 0);
-    NLA_PUT_U32(msg,
-                NL80211_ATTR_PS_STATE,
-                powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED);
-    err = nl80211_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static int
-nl80211_get_wake_on_wlan_handler(struct nl_msg *msg, void *arg)
-{
-    NMSettingWirelessWakeOnWLan *wowl = arg;
-    struct nlattr *              attrs[NL80211_ATTR_MAX + 1];
-    struct nlattr *              trig[NUM_NL80211_WOWLAN_TRIG];
-    struct genlmsghdr *          gnlh = nlmsg_data(nlmsg_hdr(msg));
-
-    nla_parse_arr(attrs, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
-
-    if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
-        return NL_SKIP;
-
-    nla_parse_arr(trig,
-                  nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
-                  nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
-                  NULL);
-
-    *wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE;
-    if (trig[NL80211_WOWLAN_TRIG_ANY])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY;
-    if (trig[NL80211_WOWLAN_TRIG_DISCONNECT])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT;
-    if (trig[NL80211_WOWLAN_TRIG_MAGIC_PKT])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC;
-    if (trig[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE;
-    if (trig[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST;
-    if (trig[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE;
-    if (trig[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE;
-    if (trig[NL80211_WOWLAN_TRIG_TCP_CONNECTION])
-        *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP;
-
-    return NL_SKIP;
-}
-
-static NMSettingWirelessWakeOnWLan
-wifi_nl80211_get_wake_on_wlan(NMWifiUtils *data)
-{
-    NMWifiUtilsNl80211 *         self = (NMWifiUtilsNl80211 *) data;
-    NMSettingWirelessWakeOnWLan  wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
-    nm_auto_nlmsg struct nl_msg *msg  = NULL;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WOWLAN, 0);
-
-    nl80211_send_and_recv(self, msg, nl80211_get_wake_on_wlan_handler, &wowl);
-
-    return wowl;
-}
-
-static gboolean
-wifi_nl80211_set_wake_on_wlan(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl)
-{
-    NMWifiUtilsNl80211 *         self = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg  = NULL;
-    struct nlattr *              triggers;
-    int                          err;
-
-    if (wowl == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE)
-        return TRUE;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_SET_WOWLAN, 0);
-
-    triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
-    if (!triggers)
-        goto nla_put_failure;
-
-    if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY))
-        NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
-    if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT))
-        NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
-    if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC))
-        NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
-    if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE))
-        NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
-    if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST))
-        NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
-    if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE))
-        NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
-    if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE))
-        NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
-
-    nla_nest_end(msg, triggers);
-
-    err = nl80211_send_and_recv(self, msg, NULL, NULL);
-
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static guint32
-wifi_nl80211_get_freq(NMWifiUtils *data)
-{
-    NMWifiUtilsNl80211 *         self       = (NMWifiUtilsNl80211 *) data;
-    struct nl80211_iface_info    iface_info = {};
-    nm_auto_nlmsg struct nl_msg *msg        = NULL;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_INTERFACE, 0);
-
-    if (nl80211_send_and_recv(self, msg, nl80211_iface_info_handler, &iface_info) < 0)
-        return 0;
-
-    return iface_info.freq;
-}
-
-static guint32
-wifi_nl80211_find_freq(NMWifiUtils *data, const guint32 *freqs)
-{
-    NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data;
-    int                 i;
-
-    for (i = 0; i < self->num_freqs; i++) {
-        while (*freqs) {
-            if (self->freqs[i] == *freqs)
-                return *freqs;
-            freqs++;
-        }
-    }
-    return 0;
-}
-
-/* @divisor: pass what value @xbm should be divided by to get dBm */
-static guint32
-nl80211_xbm_to_percent(gint32 xbm, guint32 divisor)
-{
-#define NOISE_FLOOR_DBM -90
-#define SIGNAL_MAX_DBM  -20
-
-    xbm /= divisor;
-    xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM);
-
-    return 100
-           - 70
-                 * (((float) SIGNAL_MAX_DBM - (float) xbm)
-                    / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM));
-}
-
-struct nl80211_station_info {
-    gboolean valid;
-    guint8   bssid[ETH_ALEN];
-    guint32  txrate;
-    gboolean txrate_valid;
-    guint8   signal;
-    gboolean signal_valid;
-};
-
-static int
-nl80211_station_dump_handler(struct nl_msg *msg, void *arg)
-{
-    static const struct nla_policy stats_policy[] = {
-        [NL80211_STA_INFO_INACTIVE_TIME]     = {.type = NLA_U32},
-        [NL80211_STA_INFO_RX_BYTES]          = {.type = NLA_U32},
-        [NL80211_STA_INFO_TX_BYTES]          = {.type = NLA_U32},
-        [NL80211_STA_INFO_RX_PACKETS]        = {.type = NLA_U32},
-        [NL80211_STA_INFO_TX_PACKETS]        = {.type = NLA_U32},
-        [NL80211_STA_INFO_SIGNAL]            = {.type = NLA_U8},
-        [NL80211_STA_INFO_TX_BITRATE]        = {.type = NLA_NESTED},
-        [NL80211_STA_INFO_LLID]              = {.type = NLA_U16},
-        [NL80211_STA_INFO_PLID]              = {.type = NLA_U16},
-        [NL80211_STA_INFO_PLINK_STATE]       = {.type = NLA_U8},
-        [NL80211_STA_INFO_STA_FLAGS]         = {.minlen = sizeof(struct nl80211_sta_flag_update)},
-        [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = {.type = NLA_U8},
-    };
-    static const struct nla_policy rate_policy[] = {
-        [NL80211_RATE_INFO_BITRATE]      = {.type = NLA_U16},
-        [NL80211_RATE_INFO_MCS]          = {.type = NLA_U8},
-        [NL80211_RATE_INFO_40_MHZ_WIDTH] = {.type = NLA_FLAG},
-        [NL80211_RATE_INFO_SHORT_GI]     = {.type = NLA_FLAG},
-    };
-    struct nlattr *              rinfo[G_N_ELEMENTS(rate_policy)];
-    struct nlattr *              sinfo[G_N_ELEMENTS(stats_policy)];
-    struct nl80211_station_info *info = arg;
-    struct nlattr *              tb[NL80211_ATTR_MAX + 1];
-    struct genlmsghdr *          gnlh = nlmsg_data(nlmsg_hdr(msg));
-
-    if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
-        return NL_SKIP;
-
-    if (tb[NL80211_ATTR_MAC] == NULL)
-        return NL_SKIP;
-
-    if (tb[NL80211_ATTR_STA_INFO] == NULL)
-        return NL_SKIP;
-
-    if (nla_parse_nested_arr(sinfo, tb[NL80211_ATTR_STA_INFO], stats_policy))
-        return NL_SKIP;
-
-    if (sinfo[NL80211_STA_INFO_STA_FLAGS] != NULL) {
-        const struct nl80211_sta_flag_update *flags = nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]);
-
-        if (flags->mask & ~flags->set & (1 << NL80211_STA_FLAG_ASSOCIATED))
-            return NL_SKIP;
-    }
-
-    memcpy(info->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-    info->valid = TRUE;
-
-    if (sinfo[NL80211_STA_INFO_TX_BITRATE] != NULL
-        && !nla_parse_nested_arr(rinfo, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)
-        && rinfo[NL80211_RATE_INFO_BITRATE] != NULL) {
-        /* convert from nl80211's units of 100kbps to NM's kbps */
-        info->txrate       = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
-        info->txrate_valid = TRUE;
-    }
-
-    if (sinfo[NL80211_STA_INFO_SIGNAL] != NULL) {
-        info->signal =
-            nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]), 1);
-        info->signal_valid = TRUE;
-    } else if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG] != NULL) {
-        /* Fall back to beacon signal strength */
-        info->signal =
-            nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]),
-                                   1);
-        info->signal_valid = TRUE;
-    }
-
-    return NL_SKIP;
-}
-
-static gboolean
-wifi_nl80211_get_station(NMWifiUtils *data,
-                         NMEtherAddr *out_bssid,
-                         int *        out_quality,
-                         guint32 *    out_rate)
-{
-    NMWifiUtilsNl80211 *         self     = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg      = NULL;
-    struct nl80211_station_info  sta_info = {};
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_STATION, NLM_F_DUMP);
-
-    nl80211_send_and_recv(self, msg, nl80211_station_dump_handler, &sta_info);
-
-    if (!sta_info.valid || (out_quality && !sta_info.signal_valid)
-        || (out_rate && !sta_info.txrate_valid))
-        return FALSE;
-
-    if (out_bssid)
-        memcpy(out_bssid, sta_info.bssid, ETH_ALEN);
-
-    if (out_quality)
-        *out_quality = sta_info.signal;
-
-    if (out_rate)
-        *out_rate = sta_info.txrate;
-
-    return TRUE;
-}
-
-static gboolean
-wifi_nl80211_indicate_addressing_running(NMWifiUtils *data, gboolean running)
-{
-    NMWifiUtilsNl80211 *         self = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg  = NULL;
-    int                          err;
-
-    msg = nl80211_alloc_msg(self,
-                            running ? 98 /* NL80211_CMD_CRIT_PROTOCOL_START */
-                                    : 99 /* NL80211_CMD_CRIT_PROTOCOL_STOP */,
-                            0);
-    /* Despite the DHCP name, we're using this for any type of IP addressing,
-     * DHCPv4, DHCPv6, and IPv6 SLAAC.
-     */
-    NLA_PUT_U16(msg, 179 /* NL80211_ATTR_CRIT_PROT_ID */, 1 /* NL80211_CRIT_PROTO_DHCP */);
-    if (running) {
-        /* Give DHCP 5 seconds to complete */
-        NLA_PUT_U16(msg, 180 /* NL80211_ATTR_MAX_CRIT_PROT_DURATION */, 5000);
-    }
-
-    err = nl80211_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-struct nl80211_device_info {
-    NMWifiUtilsNl80211 *self;
-    int                 phy;
-    guint32 *           freqs;
-    int                 num_freqs;
-    guint32             freq;
-    guint32             caps;
-    gboolean            can_scan;
-    gboolean            can_scan_ssid;
-    gboolean            supported;
-    gboolean            success;
-    gboolean            can_wowlan;
-};
-
-#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
-#define WLAN_CIPHER_SUITE_WEP40     0x000FAC01
-#define WLAN_CIPHER_SUITE_TKIP      0x000FAC02
-#define WLAN_CIPHER_SUITE_CCMP      0x000FAC04
-#define WLAN_CIPHER_SUITE_WEP104    0x000FAC05
-#define WLAN_CIPHER_SUITE_AES_CMAC  0x000FAC06
-#define WLAN_CIPHER_SUITE_GCMP      0x000FAC08
-#define WLAN_CIPHER_SUITE_SMS4      0x00147201
-
-static int
-nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg)
-{
-    static const struct nla_policy freq_policy[] = {
-        [NL80211_FREQUENCY_ATTR_FREQ]     = {.type = NLA_U32},
-        [NL80211_FREQUENCY_ATTR_DISABLED] = {.type = NLA_FLAG},
-#ifdef NL80211_FREQUENCY_ATTR_NO_IR
-        [NL80211_FREQUENCY_ATTR_NO_IR] = {.type = NLA_FLAG},
-#else
-        [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = {.type = NLA_FLAG},
-        [NL80211_FREQUENCY_ATTR_NO_IBSS]      = {.type = NLA_FLAG},
-#endif
-        [NL80211_FREQUENCY_ATTR_RADAR]        = {.type = NLA_FLAG},
-        [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = {.type = NLA_U32},
-    };
-    struct nlattr *             tb[NL80211_ATTR_MAX + 1];
-    struct genlmsghdr *         gnlh = nlmsg_data(nlmsg_hdr(msg));
-    struct nl80211_device_info *info = arg;
-    NMWifiUtilsNl80211 *        self = info->self;
-    struct nlattr *             tb_band[NL80211_BAND_ATTR_MAX + 1];
-    struct nlattr *             tb_freq[G_N_ELEMENTS(freq_policy)];
-    struct nlattr *             nl_band;
-    struct nlattr *             nl_freq;
-    int                         rem_freq;
-    int                         rem_band;
-    int                         freq_idx;
-
-#ifdef NL80211_FREQUENCY_ATTR_NO_IR
-    G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR
-                         && NL80211_FREQUENCY_ATTR_NO_IBSS == NL80211_FREQUENCY_ATTR_NO_IR);
-#else
-    G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN != NL80211_FREQUENCY_ATTR_NO_IBSS);
-#endif
-
-    if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
-        return NL_SKIP;
-
-    if (tb[NL80211_ATTR_WIPHY] == NULL || tb[NL80211_ATTR_WIPHY_BANDS] == NULL)
-        return NL_SKIP;
-
-    info->phy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
-
-    if (tb[NL80211_ATTR_WIPHY_FREQ])
-        info->freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
-    else
-        info->freq = 0;
-
-    if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) {
-        info->can_scan_ssid = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) > 0;
-    } else {
-        /* old kernel that only had mac80211, so assume it can */
-        info->can_scan_ssid = TRUE;
-    }
-
-    if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
-        struct nlattr *nl_cmd;
-        int            i;
-
-        nla_for_each_nested (nl_cmd, tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
-            switch (nla_get_u32(nl_cmd)) {
-            case NL80211_CMD_TRIGGER_SCAN:
-                info->can_scan = TRUE;
-                break;
-            case NL80211_CMD_CONNECT:
-            case NL80211_CMD_AUTHENTICATE:
-                /* Only devices that support CONNECT or AUTH actually support
-                 * 802.11, unlike say ipw2x00 (up to at least kernel 3.4) which
-                 * has minimal info support, but no actual command support.
-                 * This check mirrors what wpa_supplicant does to determine
-                 * whether or not to use the nl80211 driver.
-                 */
-                info->supported = TRUE;
-                break;
-            default:
-                break;
-            }
-        }
-    }
-
-    /* Find number of supported frequencies */
-    info->num_freqs = 0;
-
-    nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-        if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0)
-            return NL_SKIP;
-
-        nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
-            if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
-                continue;
-
-            if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                continue;
-
-            info->num_freqs++;
-        }
-    }
-
-    /* Read supported frequencies */
-    info->freqs = g_malloc0(sizeof(guint32) * info->num_freqs);
-
-    freq_idx = 0;
-    nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-        if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0)
-            return NL_SKIP;
-
-        nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
-            if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
-                continue;
-
-            if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                continue;
-
-            info->freqs[freq_idx] = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
-
-            info->caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID;
-
-            if (info->freqs[freq_idx] > 2400 && info->freqs[freq_idx] < 2500)
-                info->caps |= NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
-            if (info->freqs[freq_idx] > 4900 && info->freqs[freq_idx] < 6000)
-                info->caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
-
-            freq_idx++;
-        }
-    }
-
-    /* Read security/encryption support */
-    if (tb[NL80211_ATTR_CIPHER_SUITES]) {
-        guint32 *ciphers = nla_data(tb[NL80211_ATTR_CIPHER_SUITES]);
-        guint    i, num;
-
-        num = nla_len(tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof(guint32);
-        for (i = 0; i < num; i++) {
-            switch (ciphers[i]) {
-            case WLAN_CIPHER_SUITE_WEP40:
-                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40;
-                break;
-            case WLAN_CIPHER_SUITE_WEP104:
-                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
-                break;
-            case WLAN_CIPHER_SUITE_TKIP:
-                info->caps |= (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_WPA);
-                break;
-            case WLAN_CIPHER_SUITE_CCMP:
-                info->caps |= (NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN);
-                break;
-            case WLAN_CIPHER_SUITE_AES_CMAC:
-            case WLAN_CIPHER_SUITE_GCMP:
-            case WLAN_CIPHER_SUITE_SMS4:
-                break;
-            default:
-                _LOGD("don't know the meaning of NL80211_ATTR_CIPHER_SUITE %#8.8x.", ciphers[i]);
-                break;
-            }
-        }
-    }
-
-    if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
-        struct nlattr *nl_mode;
-        int            i;
-
-        nla_for_each_nested (nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
-            switch (nla_type(nl_mode)) {
-            case NL80211_IFTYPE_AP:
-                info->caps |= NM_WIFI_DEVICE_CAP_AP;
-                break;
-            case NL80211_IFTYPE_ADHOC:
-                info->caps |= NM_WIFI_DEVICE_CAP_ADHOC;
-                break;
-            case NL80211_IFTYPE_MESH_POINT:
-                info->caps |= NM_WIFI_DEVICE_CAP_MESH;
-                break;
-            }
-        }
-    }
-
-    if (tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED])
-        info->can_wowlan = TRUE;
-
-    if (tb[NL80211_ATTR_SUPPORT_IBSS_RSN])
-        info->caps |= NM_WIFI_DEVICE_CAP_IBSS_RSN;
-
-    info->success = TRUE;
-
-    return NL_SKIP;
-}
-
-static guint32
-wifi_nl80211_get_mesh_channel(NMWifiUtils *data)
-{
-    NMWifiUtilsNl80211 *         self        = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg         = NULL;
-    struct nl80211_device_info   device_info = {.self = self};
-    int                          i;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, 0);
-
-    if (nl80211_send_and_recv(self, msg, nl80211_wiphy_info_handler, &device_info) < 0) {
-        _LOGW("NL80211_CMD_GET_WIPHY request failed");
-        return 0;
-    }
-
-    for (i = 0; i < self->num_freqs; i++) {
-        if (device_info.freq == self->freqs[i])
-            return i + 1;
-    }
-    return 0;
-}
-
-static gboolean
-wifi_nl80211_set_mesh_channel(NMWifiUtils *data, guint32 channel)
-{
-    NMWifiUtilsNl80211 *         self = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg  = NULL;
-    int                          err;
-
-    if (channel > self->num_freqs)
-        return FALSE;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_SET_WIPHY, 0);
-    NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, self->freqs[channel - 1]);
-    err = nl80211_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static gboolean
-wifi_nl80211_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len)
-{
-    NMWifiUtilsNl80211 *         self = (NMWifiUtilsNl80211 *) data;
-    nm_auto_nlmsg struct nl_msg *msg  = NULL;
-    int                          err;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_SET_INTERFACE, 0);
-    NLA_PUT(msg, NL80211_ATTR_MESH_ID, len, ssid);
-    err = nl80211_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-static void
-nm_wifi_utils_nl80211_init(NMWifiUtilsNl80211 *self)
-{}
-
-static void
-nm_wifi_utils_nl80211_class_init(NMWifiUtilsNl80211Class *klass)
-{
-    GObjectClass *    object_class     = G_OBJECT_CLASS(klass);
-    NMWifiUtilsClass *wifi_utils_class = NM_WIFI_UTILS_CLASS(klass);
-
-    object_class->dispose = dispose;
-
-    wifi_utils_class->get_mode                    = wifi_nl80211_get_mode;
-    wifi_utils_class->set_mode                    = wifi_nl80211_set_mode;
-    wifi_utils_class->set_powersave               = wifi_nl80211_set_powersave;
-    wifi_utils_class->get_wake_on_wlan            = wifi_nl80211_get_wake_on_wlan,
-    wifi_utils_class->set_wake_on_wlan            = wifi_nl80211_set_wake_on_wlan,
-    wifi_utils_class->get_freq                    = wifi_nl80211_get_freq;
-    wifi_utils_class->find_freq                   = wifi_nl80211_find_freq;
-    wifi_utils_class->get_station                 = wifi_nl80211_get_station;
-    wifi_utils_class->indicate_addressing_running = wifi_nl80211_indicate_addressing_running;
-    wifi_utils_class->get_mesh_channel            = wifi_nl80211_get_mesh_channel;
-    wifi_utils_class->set_mesh_channel            = wifi_nl80211_set_mesh_channel;
-    wifi_utils_class->set_mesh_ssid               = wifi_nl80211_set_mesh_ssid;
-}
-
-NMWifiUtils *
-nm_wifi_utils_nl80211_new(int ifindex, struct nl_sock *genl)
-{
-    gs_unref_object NMWifiUtilsNl80211 *self        = NULL;
-    nm_auto_nlmsg struct nl_msg *       msg         = NULL;
-    struct nl80211_device_info          device_info = {};
-
-    if (!genl)
-        return NULL;
-
-    self = g_object_new(NM_TYPE_WIFI_UTILS_NL80211, NULL);
-
-    self->parent.ifindex = ifindex;
-    self->nl_sock        = genl;
-
-    self->id = genl_ctrl_resolve(self->nl_sock, "nl80211");
-    if (self->id < 0) {
-        _LOGD("genl_ctrl_resolve: failed to resolve \"nl80211\"");
-        return NULL;
-    }
-
-    self->phy = -1;
-
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, 0);
-
-    device_info.self = self;
-    if (nl80211_send_and_recv(self, msg, nl80211_wiphy_info_handler, &device_info) < 0) {
-        _LOGD("NL80211_CMD_GET_WIPHY request failed");
-        return NULL;
-    }
-
-    if (!device_info.success) {
-        _LOGD("NL80211_CMD_GET_WIPHY request indicated failure");
-        return NULL;
-    }
-
-    if (!device_info.supported) {
-        _LOGD("driver does not fully support nl80211, falling back to WEXT");
-        return NULL;
-    }
-
-    if (!device_info.can_scan_ssid) {
-        _LOGE("driver does not support SSID scans");
-        return NULL;
-    }
-
-    if (device_info.num_freqs == 0 || device_info.freqs == NULL) {
-        _LOGE("driver reports no supported frequencies");
-        return NULL;
-    }
-
-    if (device_info.caps == 0) {
-        _LOGE("driver doesn't report support of any encryption");
-        return NULL;
-    }
-
-    self->phy         = device_info.phy;
-    self->freqs       = device_info.freqs;
-    self->num_freqs   = device_info.num_freqs;
-    self->parent.caps = device_info.caps;
-    self->can_wowlan  = device_info.can_wowlan;
-
-    _LOGD("using nl80211 for Wi-Fi device control");
-    return (NMWifiUtils *) g_steal_pointer(&self);
-}
diff --git a/src/core/platform/wifi/nm-wifi-utils-nl80211.h b/src/core/platform/wifi/nm-wifi-utils-nl80211.h
deleted file mode 100644
index 37c32ed7..00000000
--- a/src/core/platform/wifi/nm-wifi-utils-nl80211.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2011 Intel Corporation. All rights reserved.
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#ifndef __WIFI_UTILS_NL80211_H__
-#define __WIFI_UTILS_NL80211_H__
-
-#include "nm-wifi-utils.h"
-#include "nm-platform/nm-netlink.h"
-
-#define NM_TYPE_WIFI_UTILS_NL80211 (nm_wifi_utils_nl80211_get_type())
-#define NM_WIFI_UTILS_NL80211(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211))
-#define NM_WIFI_UTILS_NL80211_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class))
-#define NM_IS_WIFI_UTILS_NL80211(obj) \
-    (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS_NL80211))
-#define NM_IS_WIFI_UTILS_NL80211_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_UTILS_NL80211))
-#define NM_WIFI_UTILS_NL80211_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class))
-
-GType nm_wifi_utils_nl80211_get_type(void);
-
-NMWifiUtils *nm_wifi_utils_nl80211_new(int ifindex, struct nl_sock *genl);
-
-#endif /* __WIFI_UTILS_NL80211_H__ */
diff --git a/src/core/platform/wifi/nm-wifi-utils-private.h b/src/core/platform/wifi/nm-wifi-utils-private.h
deleted file mode 100644
index 9ed57453..00000000
--- a/src/core/platform/wifi/nm-wifi-utils-private.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2011 - 2018 Red Hat, Inc.
- */
-
-#ifndef __WIFI_UTILS_PRIVATE_H__
-#define __WIFI_UTILS_PRIVATE_H__
-
-#include "nm-dbus-interface.h"
-#include "nm-wifi-utils.h"
-
-typedef struct {
-    GObjectClass parent;
-
-    NM80211Mode (*get_mode)(NMWifiUtils *data);
-
-    gboolean (*set_mode)(NMWifiUtils *data, const NM80211Mode mode);
-
-    /* Set power saving mode on an interface */
-    gboolean (*set_powersave)(NMWifiUtils *data, guint32 powersave);
-
-    /* Get WakeOnWLAN configuration on an interface */
-    NMSettingWirelessWakeOnWLan (*get_wake_on_wlan)(NMWifiUtils *data);
-
-    /* Set WakeOnWLAN mode on an interface */
-    gboolean (*set_wake_on_wlan)(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl);
-
-    /* Return current frequency in MHz (really associated BSS frequency) */
-    guint32 (*get_freq)(NMWifiUtils *data);
-
-    /* Return first supported frequency in the zero-terminated list */
-    guint32 (*find_freq)(NMWifiUtils *data, const guint32 *freqs);
-
-    /*
-     * @out_bssid: must be NULL or an ETH_ALEN-byte buffer
-     * @out_quality: receives signal strength percentage 0 - 100% for the current BSSID, if not NULL
-     * @out_rate: receives current bitrate in Kbps if not NULL
-     *
-     * Returns %TRUE on succcess, %FALSE on errors or if not associated.
-     */
-    gboolean (*get_station)(NMWifiUtils *data,
-                            NMEtherAddr *out_bssid,
-                            int *        out_quality,
-                            guint32 *    out_rate);
-
-    /* OLPC Mesh-only functions */
-
-    guint32 (*get_mesh_channel)(NMWifiUtils *data);
-
-    /* channel == 0 means "auto channel" */
-    gboolean (*set_mesh_channel)(NMWifiUtils *data, guint32 channel);
-
-    /* ssid == NULL means "auto SSID" */
-    gboolean (*set_mesh_ssid)(NMWifiUtils *data, const guint8 *ssid, gsize len);
-
-    gboolean (*indicate_addressing_running)(NMWifiUtils *data, gboolean running);
-} NMWifiUtilsClass;
-
-struct NMWifiUtils {
-    GObject parent;
-
-    int                      ifindex;
-    NMDeviceWifiCapabilities caps;
-};
-
-#endif /* __WIFI_UTILS_PRIVATE_H__ */
diff --git a/src/core/platform/wifi/nm-wifi-utils-wext.c b/src/core/platform/wifi/nm-wifi-utils-wext.c
deleted file mode 100644
index a44fcf62..00000000
--- a/src/core/platform/wifi/nm-wifi-utils-wext.c
+++ /dev/null
@@ -1,836 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2005 - 2018 Red Hat, Inc.
- * Copyright (C) 2006 - 2008 Novell, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-wifi-utils-wext.h"
-
-#include <sys/ioctl.h>
-#include <net/ethernet.h>
-#include <unistd.h>
-
-/* Hacks necessary to #include wireless.h; yay for WEXT */
-#ifndef __user
-    #define __user
-#endif
-#include <sys/types.h>
-#include <linux/types.h>
-#include <sys/socket.h>
-#include <linux/wireless.h>
-
-#include "nm-wifi-utils-private.h"
-#include "nm-utils.h"
-#include "nm-platform/nm-platform-utils.h"
-#include "nm-core-internal.h"
-#include "nm-core-utils.h"
-
-typedef struct {
-    NMWifiUtils       parent;
-    int               fd;
-    struct iw_quality max_qual;
-    gint8             num_freqs;
-    guint32           freqs[IW_MAX_FREQUENCIES];
-} NMWifiUtilsWext;
-
-typedef struct {
-    NMWifiUtilsClass parent;
-} NMWifiUtilsWextClass;
-
-G_DEFINE_TYPE(NMWifiUtilsWext, nm_wifi_utils_wext, NM_TYPE_WIFI_UTILS)
-
-/* Until a new wireless-tools comes out that has the defs and the structure,
- * need to copy them here.
- */
-/* Scan capability flags - in (struct iw_range *)->scan_capa */
-#define NM_IW_SCAN_CAPA_NONE  0x00
-#define NM_IW_SCAN_CAPA_ESSID 0x01
-
-struct iw_range_with_scan_capa {
-    guint32 throughput;
-    guint32 min_nwid;
-    guint32 max_nwid;
-    guint16 old_num_channels;
-    guint8  old_num_frequency;
-
-    guint8 scan_capa;
-    /* don't need the rest... */
-};
-
-#define _NMLOG_PREFIX_NAME "wifi-wext"
-#define _NMLOG(level, domain, ...)                                    \
-    G_STMT_START                                                      \
-    {                                                                 \
-        nm_log((level),                                               \
-               (domain),                                              \
-               NULL,                                                  \
-               NULL,                                                  \
-               "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),             \
-               _NMLOG_PREFIX_NAME _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
-    }                                                                 \
-    G_STMT_END
-
-static guint32
-iw_freq_to_uint32(const struct iw_freq *freq)
-{
-    if (freq->e == 0) {
-        /* Some drivers report channel not frequency.  Convert to a
-         * frequency; but this assumes that the device is in b/g mode.
-         */
-        if ((freq->m >= 1) && (freq->m <= 13))
-            return 2407 + (5 * freq->m);
-        else if (freq->m == 14)
-            return 2484;
-    }
-    return (guint32)((((double) freq->m) * nm_utils_exp10(freq->e)) / 1000000.0);
-}
-
-static void
-dispose(GObject *object)
-{
-    NMWifiUtilsWext *wext = NM_WIFI_UTILS_WEXT(object);
-
-    wext->fd = nm_close(wext->fd);
-}
-
-static gboolean
-get_ifname(int ifindex, char *buffer, const char *op)
-{
-    int errsv;
-
-    if (!nmp_utils_if_indextoname(ifindex, buffer)) {
-        errsv = errno;
-        _LOGW(LOGD_PLATFORM | LOGD_WIFI,
-              "error getting interface name for ifindex %d, operation '%s': %s (%d)",
-              ifindex,
-              op,
-              nm_strerror_native(errsv),
-              errsv);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-static NM80211Mode
-wifi_wext_get_mode_ifname(NMWifiUtils *data, const char *ifname)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    int              errsv;
-
-    memset(&wrq, 0, sizeof(struct iwreq));
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-
-    if (ioctl(wext->fd, SIOCGIWMODE, &wrq) < 0) {
-        errsv = errno;
-        if (errsv != ENODEV) {
-            _LOGW(LOGD_PLATFORM | LOGD_WIFI, "(%s): error %d getting card mode", ifname, errsv);
-        }
-        return NM_802_11_MODE_UNKNOWN;
-    }
-
-    switch (wrq.u.mode) {
-    case IW_MODE_ADHOC:
-        return NM_802_11_MODE_ADHOC;
-    case IW_MODE_MASTER:
-        return NM_802_11_MODE_AP;
-    case IW_MODE_INFRA:
-    case IW_MODE_AUTO: /* hack for WEXT devices reporting IW_MODE_AUTO */
-        return NM_802_11_MODE_INFRA;
-    default:
-        break;
-    }
-    return NM_802_11_MODE_UNKNOWN;
-}
-
-static NM80211Mode
-wifi_wext_get_mode(NMWifiUtils *data)
-{
-    char ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "get-mode"))
-        return FALSE;
-
-    return wifi_wext_get_mode_ifname(data, ifname);
-}
-
-static gboolean
-wifi_wext_set_mode(NMWifiUtils *data, const NM80211Mode mode)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    char             ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "set-mode"))
-        return FALSE;
-
-    if (wifi_wext_get_mode_ifname(data, ifname) == mode)
-        return TRUE;
-
-    memset(&wrq, 0, sizeof(struct iwreq));
-    switch (mode) {
-    case NM_802_11_MODE_ADHOC:
-        wrq.u.mode = IW_MODE_ADHOC;
-        break;
-    case NM_802_11_MODE_AP:
-        wrq.u.mode = IW_MODE_MASTER;
-        break;
-    case NM_802_11_MODE_INFRA:
-        wrq.u.mode = IW_MODE_INFRA;
-        break;
-    default:
-        g_warn_if_reached();
-        return FALSE;
-    }
-
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    if (ioctl(wext->fd, SIOCSIWMODE, &wrq) < 0) {
-        if (errno != ENODEV) {
-            _LOGE(LOGD_PLATFORM | LOGD_WIFI, "(%s): error setting mode %d", ifname, mode);
-        }
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-static gboolean
-wifi_wext_set_powersave(NMWifiUtils *data, guint32 powersave)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    char             ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "set-powersave"))
-        return FALSE;
-
-    memset(&wrq, 0, sizeof(struct iwreq));
-    if (powersave == 1) {
-        wrq.u.power.flags = IW_POWER_ALL_R;
-    } else
-        wrq.u.power.disabled = 1;
-
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    if (ioctl(wext->fd, SIOCSIWPOWER, &wrq) < 0) {
-        if (errno != ENODEV) {
-            _LOGE(LOGD_PLATFORM | LOGD_WIFI,
-                  "(%s): error setting powersave %" G_GUINT32_FORMAT,
-                  ifname,
-                  powersave);
-        }
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-static guint32
-wifi_wext_get_freq(NMWifiUtils *data)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    char             ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "get-freq"))
-        return FALSE;
-
-    memset(&wrq, 0, sizeof(struct iwreq));
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    if (ioctl(wext->fd, SIOCGIWFREQ, &wrq) < 0) {
-        _LOGW(LOGD_PLATFORM | LOGD_WIFI,
-              "(%s): error getting frequency: %s",
-              ifname,
-              nm_strerror_native(errno));
-        return 0;
-    }
-
-    return iw_freq_to_uint32(&wrq.u.freq);
-}
-
-static guint32
-wifi_wext_find_freq(NMWifiUtils *data, const guint32 *freqs)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    int              i;
-
-    for (i = 0; i < wext->num_freqs; i++) {
-        while (*freqs) {
-            if (wext->freqs[i] == *freqs)
-                return *freqs;
-            freqs++;
-        }
-    }
-    return 0;
-}
-
-static gboolean
-wifi_wext_get_bssid(NMWifiUtils *data, NMEtherAddr *out_bssid)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    char             ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "get-bssid"))
-        return FALSE;
-
-    memset(&wrq, 0, sizeof(wrq));
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    if (ioctl(wext->fd, SIOCGIWAP, &wrq) < 0) {
-        _LOGW(LOGD_PLATFORM | LOGD_WIFI,
-              "(%s): error getting associated BSSID: %s",
-              ifname,
-              nm_strerror_native(errno));
-        return FALSE;
-    }
-    memcpy(out_bssid, &(wrq.u.ap_addr.sa_data), ETH_ALEN);
-    return TRUE;
-}
-
-static guint32
-wifi_wext_get_rate(NMWifiUtils *data)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    int              err;
-    char             ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "get-rate"))
-        return FALSE;
-
-    memset(&wrq, 0, sizeof(wrq));
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    err = ioctl(wext->fd, SIOCGIWRATE, &wrq);
-    return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0);
-}
-
-static int
-wext_qual_to_percent(const struct iw_quality *qual, const struct iw_quality *max_qual)
-{
-    int percent       = -1;
-    int level_percent = -1;
-
-    g_return_val_if_fail(qual != NULL, -1);
-    g_return_val_if_fail(max_qual != NULL, -1);
-
-    /* Magically convert the many different WEXT quality representations to a percentage */
-
-    _LOGD(LOGD_WIFI,
-          "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X  ** MAX: qual "
-          "%d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X",
-          (__s8) qual->qual,
-          qual->qual,
-          qual->qual,
-          (__s8) qual->level,
-          qual->level,
-          qual->level,
-          (__s8) qual->noise,
-          qual->noise,
-          qual->noise,
-          qual->updated,
-          (__s8) max_qual->qual,
-          max_qual->qual,
-          max_qual->qual,
-          (__s8) max_qual->level,
-          max_qual->level,
-          max_qual->level,
-          (__s8) max_qual->noise,
-          max_qual->noise,
-          max_qual->noise,
-          max_qual->updated);
-
-    /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is.
-     * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be
-     * bounded by 0 and max_qual->qual, and MUST change in a linear fashion.  Within those bounds, drivers
-     * are free to use whatever they want to calculate "Link Quality".
-     */
-    if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID)
-        && !(qual->updated & IW_QUAL_QUAL_INVALID))
-        percent = (int) (100 * ((double) qual->qual / (double) max_qual->qual));
-
-    /* If the driver doesn't specify a complete and valid quality, we have two options:
-     *
-     * 1) dBm: driver must specify max_qual->level = 0, and have valid values for
-     *        qual->level and (qual->noise OR max_qual->noise)
-     * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for
-     *        qual->level and max_qual->level
-     *
-     * This is the WEXT spec.  If this interpretation is wrong, I'll fix it.  Otherwise,
-     * If drivers don't conform to it, they are wrong and need to be fixed.
-     */
-
-    if ((max_qual->level == 0)
-        && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */
-        && !(qual->updated & IW_QUAL_LEVEL_INVALID)     /* Must have valid qual->level */
-        && (((max_qual->noise > 0)
-             && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */
-            || ((qual->noise > 0)
-                && !(qual->updated & IW_QUAL_NOISE_INVALID))) /*    OR valid qual->noise */
-    ) {
-/* Absolute power values (dBm) */
-
-/* Reasonable fallbacks for dumb drivers that don't specify either level. */
-#define FALLBACK_NOISE_FLOOR_DBM -90
-#define FALLBACK_SIGNAL_MAX_DBM  -20
-        int max_level = FALLBACK_SIGNAL_MAX_DBM;
-        int noise     = FALLBACK_NOISE_FLOOR_DBM;
-        int level     = qual->level - 0x100;
-
-        level = CLAMP(level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
-
-        if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))
-            noise = qual->noise - 0x100;
-        else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID))
-            noise = max_qual->noise - 0x100;
-        noise = CLAMP(noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM - 1);
-
-        /* A sort of signal-to-noise ratio calculation */
-        level_percent = (int) (100
-                               - 70
-                                     * (((double) max_level - (double) level)
-                                        / ((double) max_level - (double) noise)));
-        _LOGD(LOGD_WIFI,
-              "QL1: level_percent is %d.  max_level %d, level %d, noise_floor %d.",
-              level_percent,
-              max_level,
-              level,
-              noise);
-    } else if ((max_qual->level != 0)
-               && !(max_qual->updated
-                    & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */
-               && !(qual->updated & IW_QUAL_LEVEL_INVALID)) {
-        /* Relative power values (RSSI) */
-
-        int level = qual->level;
-
-        /* Signal level is relavtive (0 -> max_qual->level) */
-        level         = CLAMP(level, 0, max_qual->level);
-        level_percent = (int) (100 * ((double) level / (double) max_qual->level));
-        _LOGD(LOGD_WIFI,
-              "QL2: level_percent is %d.  max_level %d, level %d.",
-              level_percent,
-              max_qual->level,
-              level);
-    } else if (percent == -1) {
-        _LOGD(LOGD_WIFI,
-              "QL: Could not get quality %% value from driver.  Driver is probably buggy.");
-    }
-
-    /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */
-    if ((percent < 1) && (level_percent >= 0))
-        percent = level_percent;
-
-    _LOGD(LOGD_WIFI, "QL: Final quality percent is %d (%d).", percent, CLAMP(percent, 0, 100));
-    return (CLAMP(percent, 0, 100));
-}
-
-static int
-wifi_wext_get_qual(NMWifiUtils *data)
-{
-    NMWifiUtilsWext *    wext = (NMWifiUtilsWext *) data;
-    struct iwreq         wrq;
-    struct iw_statistics stats;
-    char                 ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "get-qual"))
-        return FALSE;
-
-    memset(&stats, 0, sizeof(stats));
-    wrq.u.data.pointer = &stats;
-    wrq.u.data.length  = sizeof(stats);
-    wrq.u.data.flags   = 1; /* Clear updated flag */
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-
-    if (ioctl(wext->fd, SIOCGIWSTATS, &wrq) < 0) {
-        _LOGW(LOGD_PLATFORM | LOGD_WIFI,
-              "(%s): error getting signal strength: %s",
-              ifname,
-              nm_strerror_native(errno));
-        return -1;
-    }
-
-    return wext_qual_to_percent(&stats.qual, &wext->max_qual);
-}
-
-static gboolean
-wifi_wext_get_station(NMWifiUtils *data,
-                      NMEtherAddr *out_bssid,
-                      int *        out_quality,
-                      guint32 *    out_rate)
-{
-    NMEtherAddr local_addr;
-
-    if (!out_bssid && !out_quality && !out_rate) {
-        /* hm, the caller requested no parameter at all?
-         * Don't simply return TRUE, but at least check that
-         * we can successfully fetch the bssid. */
-        out_bssid = &local_addr;
-    }
-
-    if (out_bssid) {
-        if (!wifi_wext_get_bssid(data, out_bssid))
-            return FALSE;
-    }
-    if (out_quality) {
-        *out_quality = wifi_wext_get_qual(data);
-        if (*out_quality < 0)
-            return FALSE;
-    }
-    if (out_rate) {
-        *out_rate = wifi_wext_get_rate(data);
-        if (*out_rate == 0)
-            return FALSE;
-    }
-    return TRUE;
-}
-
-/*****************************************************************************/
-/* OLPC Mesh-only functions */
-
-static guint32
-wifi_wext_get_mesh_channel(NMWifiUtils *data)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    guint32          freq;
-    int              i;
-
-    freq = nm_wifi_utils_get_freq(data);
-    for (i = 0; i < wext->num_freqs; i++) {
-        if (freq == wext->freqs[i])
-            return i + 1;
-    }
-    return 0;
-}
-
-static gboolean
-wifi_wext_set_mesh_channel(NMWifiUtils *data, guint32 channel)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    char             ifname[IFNAMSIZ];
-
-    if (!get_ifname(data->ifindex, ifname, "set-mesh-channel"))
-        return FALSE;
-
-    memset(&wrq, 0, sizeof(struct iwreq));
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-
-    if (channel > 0) {
-        wrq.u.freq.flags = IW_FREQ_FIXED;
-        wrq.u.freq.e     = 0;
-        wrq.u.freq.m     = channel;
-    }
-
-    if (ioctl(wext->fd, SIOCSIWFREQ, &wrq) < 0) {
-        _LOGE(LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
-              "(%s): error setting channel to %d: %s",
-              ifname,
-              channel,
-              nm_strerror_native(errno));
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-static gboolean
-wifi_wext_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len)
-{
-    NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
-    struct iwreq     wrq;
-    char             buf[IW_ESSID_MAX_SIZE + 1];
-    char             ifname[IFNAMSIZ];
-    int              errsv;
-
-    if (!get_ifname(data->ifindex, ifname, "set-mesh-ssid"))
-        return FALSE;
-
-    memset(buf, 0, sizeof(buf));
-    memcpy(buf, ssid, MIN(sizeof(buf) - 1, len));
-
-    wrq.u.essid.pointer = (caddr_t) buf;
-    wrq.u.essid.length  = len;
-    wrq.u.essid.flags   = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */
-
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    if (ioctl(wext->fd, SIOCSIWESSID, &wrq) == 0)
-        return TRUE;
-
-    errsv = errno;
-    if (errsv != ENODEV) {
-        gs_free char *ssid_str = NULL;
-
-        _LOGE(LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
-              "(%s): error setting SSID to %s: %s",
-              ifname,
-              (ssid_str = _nm_utils_ssid_to_string_arr(ssid, len)),
-              nm_strerror_native(errsv));
-    }
-
-    return FALSE;
-}
-
-/*****************************************************************************/
-
-static gboolean
-wext_can_scan_ifname(NMWifiUtilsWext *wext, const char *ifname)
-{
-    struct iwreq wrq;
-
-    memset(&wrq, 0, sizeof(struct iwreq));
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    if (ioctl(wext->fd, SIOCSIWSCAN, &wrq) < 0) {
-        if (errno == EOPNOTSUPP)
-            return FALSE;
-    }
-    return TRUE;
-}
-
-static gboolean
-wext_get_range_ifname(NMWifiUtilsWext *wext,
-                      const char *     ifname,
-                      struct iw_range *range,
-                      guint32 *        response_len)
-{
-    int          i       = 26;
-    gboolean     success = FALSE;
-    struct iwreq wrq;
-    int          errsv;
-
-    memset(&wrq, 0, sizeof(struct iwreq));
-    nm_utils_ifname_cpy(wrq.ifr_name, ifname);
-    wrq.u.data.pointer = (caddr_t) range;
-    wrq.u.data.length  = sizeof(struct iw_range);
-
-    /* Need to give some drivers time to recover after suspend/resume
-     * (ex ipw3945 takes a few seconds to talk to its regulatory daemon;
-     * see rh bz#362421)
-     */
-    while (i-- > 0) {
-        if (ioctl(wext->fd, SIOCGIWRANGE, &wrq) == 0) {
-            if (response_len)
-                *response_len = wrq.u.data.length;
-            success = TRUE;
-            break;
-        } else {
-            errsv = errno;
-            if (errsv != EAGAIN) {
-                _LOGE(LOGD_PLATFORM | LOGD_WIFI,
-                      "(%s): couldn't get driver range information (%d).",
-                      ifname,
-                      errsv);
-                break;
-            }
-        }
-
-        g_usleep(G_USEC_PER_SEC / 4);
-    }
-
-    if (i <= 0) {
-        _LOGW(LOGD_PLATFORM | LOGD_WIFI,
-              "(%s): driver took too long to respond to IWRANGE query.",
-              ifname);
-    }
-
-    return success;
-}
-
-#define WPA_CAPS                                                                              \
-    (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_WPA \
-     | NM_WIFI_DEVICE_CAP_RSN)
-
-static guint32
-wext_get_caps(NMWifiUtilsWext *wext, const char *ifname, struct iw_range *range)
-{
-    guint32 caps = NM_WIFI_DEVICE_CAP_NONE;
-
-    g_return_val_if_fail(wext != NULL, NM_WIFI_DEVICE_CAP_NONE);
-    g_return_val_if_fail(range != NULL, NM_WIFI_DEVICE_CAP_NONE);
-
-    /* All drivers should support WEP by default */
-    caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
-
-    if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
-        caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP;
-
-    if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
-        caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP;
-
-    if (range->enc_capa & IW_ENC_CAPA_WPA)
-        caps |= NM_WIFI_DEVICE_CAP_WPA;
-
-    if (range->enc_capa & IW_ENC_CAPA_WPA2)
-        caps |= NM_WIFI_DEVICE_CAP_RSN;
-
-    /* Check for cipher support but not WPA support */
-    if ((caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
-        && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) {
-        _LOGW(LOGD_WIFI,
-              "%s: device supports WPA ciphers but not WPA protocol; WPA unavailable.",
-              ifname);
-        caps &= ~WPA_CAPS;
-    }
-
-    /* Check for WPA support but not cipher support */
-    if ((caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))
-        && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) {
-        _LOGW(LOGD_WIFI,
-              "%s: device supports WPA protocol but not WPA ciphers; WPA unavailable.",
-              ifname);
-        caps &= ~WPA_CAPS;
-    }
-
-    /* There's no way to detect Ad-Hoc/AP mode support with WEXT
-     * (other than actually trying to do it), so just assume that
-     * Ad-Hoc is supported and AP isn't.
-     */
-    caps |= NM_WIFI_DEVICE_CAP_ADHOC;
-
-    return caps;
-}
-
-/*****************************************************************************/
-
-static void
-nm_wifi_utils_wext_init(NMWifiUtilsWext *self)
-{}
-
-static void
-nm_wifi_utils_wext_class_init(NMWifiUtilsWextClass *klass)
-{
-    GObjectClass *    object_class     = G_OBJECT_CLASS(klass);
-    NMWifiUtilsClass *wifi_utils_class = NM_WIFI_UTILS_CLASS(klass);
-
-    object_class->dispose = dispose;
-
-    wifi_utils_class->get_mode         = wifi_wext_get_mode;
-    wifi_utils_class->set_mode         = wifi_wext_set_mode;
-    wifi_utils_class->set_powersave    = wifi_wext_set_powersave;
-    wifi_utils_class->get_freq         = wifi_wext_get_freq;
-    wifi_utils_class->find_freq        = wifi_wext_find_freq;
-    wifi_utils_class->get_station      = wifi_wext_get_station;
-    wifi_utils_class->get_mesh_channel = wifi_wext_get_mesh_channel;
-    wifi_utils_class->set_mesh_channel = wifi_wext_set_mesh_channel;
-    wifi_utils_class->set_mesh_ssid    = wifi_wext_set_mesh_ssid;
-}
-
-NMWifiUtils *
-nm_wifi_utils_wext_new(int ifindex, gboolean check_scan)
-{
-    NMWifiUtilsWext *               wext;
-    struct iw_range                 range;
-    guint32                         response_len = 0;
-    struct iw_range_with_scan_capa *scan_capa_range;
-    int                             i;
-    gboolean                        freq_valid = FALSE, has_5ghz = FALSE, has_2ghz = FALSE;
-    char                            ifname[IFNAMSIZ];
-
-    if (!nmp_utils_if_indextoname(ifindex, ifname)) {
-        _LOGW(LOGD_PLATFORM | LOGD_WIFI, "can't determine interface name for ifindex %d", ifindex);
-        return NULL;
-    }
-
-    wext = g_object_new(NM_TYPE_WIFI_UTILS_WEXT, NULL);
-
-    wext->parent.ifindex = ifindex;
-    wext->fd             = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-    if (wext->fd < 0)
-        goto error;
-
-    memset(&range, 0, sizeof(struct iw_range));
-    if (wext_get_range_ifname(wext, ifname, &range, &response_len) == FALSE) {
-        _LOGI(LOGD_PLATFORM | LOGD_WIFI, "(%s): driver WEXT range request failed", ifname);
-        goto error;
-    }
-
-    if ((response_len < 300) || (range.we_version_compiled < 21)) {
-        _LOGI(LOGD_PLATFORM | LOGD_WIFI,
-              "(%s): driver WEXT version too old (got %d, expected >= 21)",
-              ifname,
-              range.we_version_compiled);
-        goto error;
-    }
-
-    wext->max_qual.qual    = range.max_qual.qual;
-    wext->max_qual.level   = range.max_qual.level;
-    wext->max_qual.noise   = range.max_qual.noise;
-    wext->max_qual.updated = range.max_qual.updated;
-
-    wext->num_freqs = MIN(range.num_frequency, IW_MAX_FREQUENCIES);
-    for (i = 0; i < wext->num_freqs; i++) {
-        wext->freqs[i] = iw_freq_to_uint32(&range.freq[i]);
-        freq_valid     = TRUE;
-        if (wext->freqs[i] > 2400 && wext->freqs[i] < 2500)
-            has_2ghz = TRUE;
-        else if (wext->freqs[i] > 4900 && wext->freqs[i] < 6000)
-            has_5ghz = TRUE;
-    }
-
-    /* Check for scanning capability; cards that can't scan are not supported */
-    if (check_scan && (wext_can_scan_ifname(wext, ifname) == FALSE)) {
-        _LOGI(LOGD_PLATFORM | LOGD_WIFI, "(%s): drivers that cannot scan are unsupported", ifname);
-        goto error;
-    }
-
-    /* Check for the ability to scan specific SSIDs.  Until the scan_capa
-     * field gets added to wireless-tools, need to work around that by casting
-     * to the custom structure.
-     */
-    scan_capa_range = (struct iw_range_with_scan_capa *) &range;
-    if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) {
-        _LOGI(LOGD_PLATFORM | LOGD_WIFI,
-              "(%s): driver supports SSID scans (scan_capa 0x%02X).",
-              ifname,
-              scan_capa_range->scan_capa);
-    } else {
-        _LOGI(LOGD_PLATFORM | LOGD_WIFI,
-              "(%s): driver does not support SSID scans (scan_capa 0x%02X).",
-              ifname,
-              scan_capa_range->scan_capa);
-    }
-
-    wext->parent.caps = wext_get_caps(wext, ifname, &range);
-    if (freq_valid)
-        wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID;
-    if (has_2ghz)
-        wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
-    if (has_5ghz)
-        wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
-
-    _LOGI(LOGD_PLATFORM | LOGD_WIFI, "(%s): using WEXT for Wi-Fi device control", ifname);
-
-    return (NMWifiUtils *) wext;
-
-error:
-    g_object_unref(wext);
-    return NULL;
-}
-
-gboolean
-nm_wifi_utils_wext_is_wifi(const char *iface)
-{
-    int          fd;
-    struct iwreq iwr;
-    gboolean     is_wifi = FALSE;
-
-    /* performing an ioctl on a non-existing name may cause the automatic
-     * loading of kernel modules, which should be avoided.
-     *
-     * Usually, we should thus make sure that an interface with this name
-     * exists.
-     *
-     * Note that wifi_wext_is_wifi() has only one caller which just verified
-     * that an interface with this name exists.
-     */
-
-    fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-    if (fd >= 0) {
-        nm_utils_ifname_cpy(iwr.ifr_ifrn.ifrn_name, iface);
-        if (ioctl(fd, SIOCGIWNAME, &iwr) == 0)
-            is_wifi = TRUE;
-        nm_close(fd);
-    }
-    return is_wifi;
-}
diff --git a/src/core/platform/wifi/nm-wifi-utils-wext.h b/src/core/platform/wifi/nm-wifi-utils-wext.h
deleted file mode 100644
index d6f3453c..00000000
--- a/src/core/platform/wifi/nm-wifi-utils-wext.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2011 - 2018 Red Hat, Inc.
- */
-
-#ifndef __WIFI_UTILS_WEXT_H__
-#define __WIFI_UTILS_WEXT_H__
-
-#include "nm-wifi-utils.h"
-
-#define NM_TYPE_WIFI_UTILS_WEXT (nm_wifi_utils_wext_get_type())
-#define NM_WIFI_UTILS_WEXT(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWext))
-#define NM_WIFI_UTILS_WEXT_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWextClass))
-#define NM_IS_WIFI_UTILS_WEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS_WEXT))
-#define NM_IS_WIFI_UTILS_WEXT_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_UTILS_WEXT))
-#define NM_WIFI_UTILS_WEXT_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWextClass))
-
-GType nm_wifi_utils_wext_get_type(void);
-
-NMWifiUtils *nm_wifi_utils_wext_new(int ifindex, gboolean check_scan);
-
-gboolean nm_wifi_utils_wext_is_wifi(const char *iface);
-
-#endif /* __WIFI_UTILS_WEXT_H__ */
diff --git a/src/core/platform/wifi/nm-wifi-utils.c b/src/core/platform/wifi/nm-wifi-utils.c
deleted file mode 100644
index 49ae1811..00000000
--- a/src/core/platform/wifi/nm-wifi-utils.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2005 - 2018 Red Hat, Inc.
- * Copyright (C) 2006 - 2008 Novell, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-wifi-utils.h"
-
-#include <sys/stat.h>
-#include <stdio.h>
-#include <fcntl.h>
-
-#include "nm-wifi-utils-private.h"
-#include "nm-wifi-utils-nl80211.h"
-#if HAVE_WEXT
-    #include "nm-wifi-utils-wext.h"
-#endif
-#include "nm-core-utils.h"
-
-#include "nm-platform/nm-platform-utils.h"
-
-G_DEFINE_ABSTRACT_TYPE(NMWifiUtils, nm_wifi_utils, G_TYPE_OBJECT)
-
-/*****************************************************************************/
-
-static void
-nm_wifi_utils_init(NMWifiUtils *self)
-{}
-
-static void
-nm_wifi_utils_class_init(NMWifiUtilsClass *klass)
-{}
-
-NMWifiUtils *
-nm_wifi_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan)
-{
-    NMWifiUtils *ret;
-
-    g_return_val_if_fail(ifindex > 0, NULL);
-
-    ret = nm_wifi_utils_nl80211_new(ifindex, genl);
-
-#if HAVE_WEXT
-    if (ret == NULL)
-        ret = nm_wifi_utils_wext_new(ifindex, check_scan);
-#endif
-
-    return ret;
-}
-
-NMDeviceWifiCapabilities
-nm_wifi_utils_get_caps(NMWifiUtils *data)
-{
-    g_return_val_if_fail(data != NULL, NM_WIFI_DEVICE_CAP_NONE);
-
-    return data->caps;
-}
-
-NM80211Mode
-nm_wifi_utils_get_mode(NMWifiUtils *data)
-{
-    g_return_val_if_fail(data != NULL, NM_802_11_MODE_UNKNOWN);
-    return NM_WIFI_UTILS_GET_CLASS(data)->get_mode(data);
-}
-
-gboolean
-nm_wifi_utils_set_mode(NMWifiUtils *data, const NM80211Mode mode)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, FALSE);
-    g_return_val_if_fail((mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_AP)
-                             || (mode == NM_802_11_MODE_ADHOC) || (mode == NM_802_11_MODE_MESH),
-                         FALSE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-
-    /* nl80211 probably doesn't need this */
-    return klass->set_mode ? klass->set_mode(data, mode) : TRUE;
-}
-
-gboolean
-nm_wifi_utils_set_powersave(NMWifiUtils *data, guint32 powersave)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, FALSE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-    return klass->set_powersave ? klass->set_powersave(data, powersave) : TRUE;
-}
-
-NMSettingWirelessWakeOnWLan
-nm_wifi_utils_get_wake_on_wlan(NMWifiUtils *data)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-
-    return klass->get_wake_on_wlan ? klass->get_wake_on_wlan(data)
-                                   : NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
-}
-
-gboolean
-nm_wifi_utils_set_wake_on_wlan(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, FALSE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-    return klass->set_wake_on_wlan ? klass->set_wake_on_wlan(data, wowl) : FALSE;
-}
-
-guint32
-nm_wifi_utils_get_freq(NMWifiUtils *data)
-{
-    g_return_val_if_fail(data != NULL, 0);
-    return NM_WIFI_UTILS_GET_CLASS(data)->get_freq(data);
-}
-
-guint32
-nm_wifi_utils_find_freq(NMWifiUtils *data, const guint32 *freqs)
-{
-    g_return_val_if_fail(data != NULL, 0);
-    g_return_val_if_fail(freqs != NULL, 0);
-    return NM_WIFI_UTILS_GET_CLASS(data)->find_freq(data, freqs);
-}
-
-gboolean
-nm_wifi_utils_get_station(NMWifiUtils *data,
-                          NMEtherAddr *out_bssid,
-                          int *        out_quality,
-                          guint32 *    out_rate)
-{
-    g_return_val_if_fail(data != NULL, FALSE);
-
-    return NM_WIFI_UTILS_GET_CLASS(data)->get_station(data, out_bssid, out_quality, out_rate);
-}
-
-gboolean
-nm_wifi_utils_is_wifi(int dirfd, const char *ifname)
-{
-    g_return_val_if_fail(dirfd >= 0, FALSE);
-
-    if (faccessat(dirfd, "phy80211", F_OK, 0) == 0)
-        return TRUE;
-#if HAVE_WEXT
-    if (nm_wifi_utils_wext_is_wifi(ifname))
-        return TRUE;
-#endif
-    return FALSE;
-}
-
-/* OLPC Mesh-only functions */
-
-guint32
-nm_wifi_utils_get_mesh_channel(NMWifiUtils *data)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, FALSE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-    g_return_val_if_fail(klass->get_mesh_channel != NULL, FALSE);
-
-    return klass->get_mesh_channel(data);
-}
-
-gboolean
-nm_wifi_utils_set_mesh_channel(NMWifiUtils *data, guint32 channel)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, FALSE);
-    g_return_val_if_fail(channel <= 13, FALSE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-    g_return_val_if_fail(klass->set_mesh_channel != NULL, FALSE);
-
-    return klass->set_mesh_channel(data, channel);
-}
-
-gboolean
-nm_wifi_utils_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, FALSE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-    g_return_val_if_fail(klass->set_mesh_ssid != NULL, FALSE);
-
-    return klass->set_mesh_ssid(data, ssid, len);
-}
-
-gboolean
-nm_wifi_utils_indicate_addressing_running(NMWifiUtils *data, gboolean running)
-{
-    NMWifiUtilsClass *klass;
-
-    g_return_val_if_fail(data != NULL, FALSE);
-
-    klass = NM_WIFI_UTILS_GET_CLASS(data);
-    return klass->indicate_addressing_running ? klass->indicate_addressing_running(data, running)
-                                              : FALSE;
-}
diff --git a/src/core/platform/wifi/nm-wifi-utils.h b/src/core/platform/wifi/nm-wifi-utils.h
deleted file mode 100644
index a252e43e..00000000
--- a/src/core/platform/wifi/nm-wifi-utils.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2005 - 2018 Red Hat, Inc.
- * Copyright (C) 2006 - 2008 Novell, Inc.
- */
-
-#ifndef __WIFI_UTILS_H__
-#define __WIFI_UTILS_H__
-
-#include <net/ethernet.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-setting-wireless.h"
-#include "nm-platform/nm-netlink.h"
-
-typedef struct NMWifiUtils NMWifiUtils;
-
-#define NM_TYPE_WIFI_UTILS (nm_wifi_utils_get_type())
-#define NM_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS, NMWifiUtils))
-#define NM_WIFI_UTILS_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass))
-#define NM_IS_WIFI_UTILS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS))
-#define NM_IS_WIFI_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_UTILS))
-#define NM_WIFI_UTILS_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass))
-
-GType nm_wifi_utils_get_type(void);
-
-gboolean nm_wifi_utils_is_wifi(int dirfd, const char *ifname);
-
-NMWifiUtils *nm_wifi_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan);
-
-NMDeviceWifiCapabilities nm_wifi_utils_get_caps(NMWifiUtils *data);
-
-NM80211Mode nm_wifi_utils_get_mode(NMWifiUtils *data);
-
-gboolean nm_wifi_utils_set_mode(NMWifiUtils *data, const NM80211Mode mode);
-
-/* Returns frequency in MHz */
-guint32 nm_wifi_utils_get_freq(NMWifiUtils *data);
-
-/* Return the first supported frequency in the zero-terminated list.
- * Frequencies are specified in MHz. */
-guint32 nm_wifi_utils_find_freq(NMWifiUtils *data, const guint32 *freqs);
-
-/*
- * @out_bssid: must be NULL or an ETH_ALEN-byte buffer
- * @out_quality: receives signal quality in 0 - 100% range if not NULL
- * @out_rate: receives current bitrate in Kbps if not NULL
- *
- * Returns %TRUE on succcess.
- */
-gboolean nm_wifi_utils_get_station(NMWifiUtils *data,
-                                   NMEtherAddr *out_bssid,
-                                   int *        out_quality,
-                                   guint32 *    out_rate);
-
-/* Tells the driver DHCP or SLAAC is running */
-gboolean nm_wifi_utils_indicate_addressing_running(NMWifiUtils *data, gboolean running);
-
-gboolean nm_wifi_utils_set_powersave(NMWifiUtils *data, guint32 powersave);
-
-NMSettingWirelessWakeOnWLan nm_wifi_utils_get_wake_on_wlan(NMWifiUtils *data);
-
-gboolean nm_wifi_utils_set_wake_on_wlan(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl);
-
-/* OLPC Mesh-only functions */
-guint32 nm_wifi_utils_get_mesh_channel(NMWifiUtils *data);
-
-gboolean nm_wifi_utils_set_mesh_channel(NMWifiUtils *data, guint32 channel);
-
-gboolean nm_wifi_utils_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len);
-
-#endif /* __WIFI_UTILS_H__ */
diff --git a/src/core/platform/wpan/nm-wpan-utils.c b/src/core/platform/wpan/nm-wpan-utils.c
deleted file mode 100644
index efb19979..00000000
--- a/src/core/platform/wpan/nm-wpan-utils.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-wpan-utils.h"
-
-#include <linux/if.h>
-
-#include "nm-platform/nm-netlink.h"
-#include "platform/linux/nl802154.h"
-#include "nm-platform/nm-platform-utils.h"
-
-#define _NMLOG_PREFIX_NAME "wpan-nl802154"
-#define _NMLOG(level, domain, ...)                                                                \
-    G_STMT_START                                                                                  \
-    {                                                                                             \
-        char        _ifname_buf[IFNAMSIZ];                                                        \
-        const char *_ifname = self ? nmp_utils_if_indextoname(self->ifindex, _ifname_buf) : NULL; \
-                                                                                                  \
-        nm_log((level),                                                                           \
-               (domain),                                                                          \
-               _ifname ?: NULL,                                                                   \
-               NULL,                                                                              \
-               "%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                                   \
-               _NMLOG_PREFIX_NAME,                                                                \
-               NM_PRINT_FMT_QUOTED(_ifname, " (", _ifname, ")", "")                               \
-                   _NM_UTILS_MACRO_REST(__VA_ARGS__));                                            \
-    }                                                                                             \
-    G_STMT_END
-
-/*****************************************************************************/
-
-struct NMWpanUtils {
-    GObject         parent;
-    int             ifindex;
-    struct nl_sock *nl_sock;
-    int             id;
-};
-
-typedef struct {
-    GObjectClass parent;
-} NMWpanUtilsClass;
-
-G_DEFINE_TYPE(NMWpanUtils, nm_wpan_utils, G_TYPE_OBJECT)
-
-/*****************************************************************************/
-
-static int
-ack_handler(struct nl_msg *msg, void *arg)
-{
-    int *done = arg;
-    *done     = 1;
-    return NL_STOP;
-}
-
-static int
-finish_handler(struct nl_msg *msg, void *arg)
-{
-    int *done = arg;
-    *done     = 1;
-    return NL_SKIP;
-}
-
-static int
-error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
-{
-    int *done = arg;
-    *done     = err->error;
-    return NL_SKIP;
-}
-
-static struct nl_msg *
-_nl802154_alloc_msg(int id, int ifindex, guint32 cmd, guint32 flags)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-
-    msg = nlmsg_alloc();
-    genlmsg_put(msg, 0, 0, id, 0, flags, cmd, 0);
-    NLA_PUT_U32(msg, NL802154_ATTR_IFINDEX, ifindex);
-    return g_steal_pointer(&msg);
-
-nla_put_failure:
-    g_return_val_if_reached(NULL);
-}
-
-static struct nl_msg *
-nl802154_alloc_msg(NMWpanUtils *self, guint32 cmd, guint32 flags)
-{
-    return _nl802154_alloc_msg(self->id, self->ifindex, cmd, flags);
-}
-
-static int
-nl802154_send_and_recv(NMWpanUtils *  self,
-                       struct nl_msg *msg,
-                       int (*valid_handler)(struct nl_msg *, void *),
-                       void *valid_data)
-{
-    int                err;
-    int                done = 0;
-    const struct nl_cb cb   = {
-        .err_cb     = error_handler,
-        .err_arg    = &done,
-        .finish_cb  = finish_handler,
-        .finish_arg = &done,
-        .ack_cb     = ack_handler,
-        .ack_arg    = &done,
-        .valid_cb   = valid_handler,
-        .valid_arg  = valid_data,
-    };
-
-    g_return_val_if_fail(msg != NULL, -ENOMEM);
-
-    err = nl_send_auto(self->nl_sock, msg);
-    if (err < 0)
-        return err;
-
-    /* Loop until one of our NL callbacks says we're done; on success
-     * done will be 1, on error it will be < 0.
-     */
-    while (!done) {
-        err = nl_recvmsgs(self->nl_sock, &cb);
-        if (err < 0 && err != -EAGAIN) {
-            _LOGW(LOGD_PLATFORM, "nl_recvmsgs() error: (%d) %s", err, nm_strerror(err));
-            break;
-        }
-    }
-
-    if (err >= 0 && done < 0)
-        err = done;
-    return err;
-}
-
-struct nl802154_interface {
-    guint16 pan_id;
-    guint16 short_addr;
-
-    gboolean valid;
-};
-
-static int
-nl802154_get_interface_handler(struct nl_msg *msg, void *arg)
-{
-    static const struct nla_policy nl802154_policy[] = {
-        [NL802154_ATTR_PAN_ID]     = {.type = NLA_U16},
-        [NL802154_ATTR_SHORT_ADDR] = {.type = NLA_U16},
-    };
-    struct nlattr *            tb[G_N_ELEMENTS(nl802154_policy)];
-    struct nl802154_interface *info = arg;
-    struct genlmsghdr *        gnlh = nlmsg_data(nlmsg_hdr(msg));
-
-    if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nl802154_policy) < 0)
-        return NL_SKIP;
-
-    if (tb[NL802154_ATTR_PAN_ID])
-        info->pan_id = le16toh(nla_get_u16(tb[NL802154_ATTR_PAN_ID]));
-
-    if (tb[NL802154_ATTR_SHORT_ADDR])
-        info->short_addr = le16toh(nla_get_u16(tb[NL802154_ATTR_SHORT_ADDR]));
-
-    info->valid = TRUE;
-
-    return NL_SKIP;
-}
-
-static void
-nl802154_get_interface(NMWpanUtils *self, struct nl802154_interface *interface)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-
-    memset(interface, 0, sizeof(*interface));
-
-    msg = nl802154_alloc_msg(self, NL802154_CMD_GET_INTERFACE, 0);
-
-    nl802154_send_and_recv(self, msg, nl802154_get_interface_handler, interface);
-}
-
-/*****************************************************************************/
-
-guint16
-nm_wpan_utils_get_pan_id(NMWpanUtils *self)
-{
-    struct nl802154_interface interface;
-
-    nl802154_get_interface(self, &interface);
-
-    return interface.pan_id;
-}
-
-gboolean
-nm_wpan_utils_set_pan_id(NMWpanUtils *self, guint16 pan_id)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    int                          err;
-
-    g_return_val_if_fail(self != NULL, FALSE);
-
-    msg = nl802154_alloc_msg(self, NL802154_CMD_SET_PAN_ID, 0);
-    NLA_PUT_U16(msg, NL802154_ATTR_PAN_ID, htole16(pan_id));
-    err = nl802154_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-guint16
-nm_wpan_utils_get_short_addr(NMWpanUtils *self)
-{
-    struct nl802154_interface interface;
-
-    nl802154_get_interface(self, &interface);
-
-    return interface.short_addr;
-}
-
-gboolean
-nm_wpan_utils_set_short_addr(NMWpanUtils *self, guint16 short_addr)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    int                          err;
-
-    g_return_val_if_fail(self != NULL, FALSE);
-
-    msg = nl802154_alloc_msg(self, NL802154_CMD_SET_SHORT_ADDR, 0);
-    NLA_PUT_U16(msg, NL802154_ATTR_SHORT_ADDR, htole16(short_addr));
-    err = nl802154_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-gboolean
-nm_wpan_utils_set_channel(NMWpanUtils *self, guint8 page, guint8 channel)
-{
-    nm_auto_nlmsg struct nl_msg *msg = NULL;
-    int                          err;
-
-    g_return_val_if_fail(self != NULL, FALSE);
-
-    msg = nl802154_alloc_msg(self, NL802154_CMD_SET_CHANNEL, 0);
-    NLA_PUT_U8(msg, NL802154_ATTR_PAGE, page);
-    NLA_PUT_U8(msg, NL802154_ATTR_CHANNEL, channel);
-    err = nl802154_send_and_recv(self, msg, NULL, NULL);
-    return err >= 0;
-
-nla_put_failure:
-    g_return_val_if_reached(FALSE);
-}
-
-/*****************************************************************************/
-
-static void
-nm_wpan_utils_init(NMWpanUtils *self)
-{}
-
-static void
-nm_wpan_utils_class_init(NMWpanUtilsClass *klass)
-{}
-
-NMWpanUtils *
-nm_wpan_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan)
-{
-    NMWpanUtils *self;
-
-    g_return_val_if_fail(ifindex > 0, NULL);
-
-    if (!genl)
-        return NULL;
-
-    self          = g_object_new(NM_TYPE_WPAN_UTILS, NULL);
-    self->ifindex = ifindex;
-    self->nl_sock = genl;
-    self->id      = genl_ctrl_resolve(genl, "nl802154");
-
-    if (self->id < 0) {
-        _LOGD(LOGD_PLATFORM, "genl_ctrl_resolve: failed to resolve \"nl802154\"");
-        g_object_unref(self);
-        return NULL;
-    }
-
-    return self;
-}
diff --git a/src/core/platform/wpan/nm-wpan-utils.h b/src/core/platform/wpan/nm-wpan-utils.h
deleted file mode 100644
index 940c2c9d..00000000
--- a/src/core/platform/wpan/nm-wpan-utils.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#ifndef __WPAN_UTILS_H__
-#define __WPAN_UTILS_H__
-
-#include <net/ethernet.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-platform/nm-netlink.h"
-
-typedef struct NMWpanUtils NMWpanUtils;
-
-#define NM_TYPE_WPAN_UTILS (nm_wpan_utils_get_type())
-#define NM_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_UTILS, NMWpanUtils))
-#define NM_WPAN_UTILS_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass))
-#define NM_IS_WPAN_UTILS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WPAN_UTILS))
-#define NM_IS_WPAN_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WPAN_UTILS))
-#define NM_WPAN_UTILS_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass))
-
-GType nm_wpan_utils_get_type(void);
-
-NMWpanUtils *nm_wpan_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan);
-
-guint16  nm_wpan_utils_get_pan_id(NMWpanUtils *self);
-gboolean nm_wpan_utils_set_pan_id(NMWpanUtils *self, guint16 pan_id);
-
-guint16  nm_wpan_utils_get_short_addr(NMWpanUtils *self);
-gboolean nm_wpan_utils_set_short_addr(NMWpanUtils *self, guint16 short_addr);
-
-gboolean nm_wpan_utils_set_channel(NMWpanUtils *self, guint8 page, guint8 channel);
-
-#endif /* __WPAN_UTILS_H__ */
diff --git a/src/core/ppp/meson.build b/src/core/ppp/meson.build
index 607b4718..ffeb0eba 100644
--- a/src/core/ppp/meson.build
+++ b/src/core/ppp/meson.build
@@ -4,9 +4,19 @@ nm_pppd_plugin = shared_module(
   'nm-pppd-plugin',
   name_prefix: '',
   sources: 'nm-pppd-plugin.c',
-  dependencies: libnm_core_nm_default_dep,
-  c_args: [
-    '-DG_LOG_DOMAIN="nm-pppd-plugin"',
+  include_directories: [
+    src_inc,
+    top_inc,
+  ],
+  dependencies: [
+    libnm_core_public_dep,
+    glib_dep,
+  ],
+  link_with: [
+    libnm_core_impl,
+    libnm_glib_aux,
+    libnm_std_aux,
+    libc_siphash,
   ],
   install: true,
   install_dir: pppd_plugin_dir,
@@ -20,7 +30,6 @@ core_plugins += shared_module(
     'nm-ppp-manager.c',
   ],
   dependencies: core_plugin_dep,
-  c_args: daemon_c_flags,
   link_args: '-Wl,--version-script,@0@'.format(linker_script),
   link_depends: linker_script,
   install: true,
diff --git a/src/core/ppp/nm-ppp-manager-call.c b/src/core/ppp/nm-ppp-manager-call.c
index f791e8b0..5e84f596 100644
--- a/src/core/ppp/nm-ppp-manager-call.c
+++ b/src/core/ppp/nm-ppp-manager-call.c
@@ -18,17 +18,21 @@
 
 /*****************************************************************************/
 
-static NMPPPOps *ppp_ops = NULL;
+static const NMPPPOps *_ppp_ops = NULL;
+
+#define ppp_ops_get() ((const NMPPPOps *) g_atomic_pointer_get(&_ppp_ops))
 
 NMPPPManager *
 nm_ppp_manager_create(const char *iface, GError **error)
 {
-    NMPPPManager *ret;
-    GModule *     plugin;
-    GError *      error_local = NULL;
-    NMPPPOps *    ops;
-    struct stat   st;
-
+    NMPPPManager *  ret;
+    GModule *       plugin;
+    GError *        error_local = NULL;
+    struct stat     st;
+    const NMPPPOps *ppp_ops;
+
+again:
+    ppp_ops = ppp_ops_get();
     if (G_UNLIKELY(!ppp_ops)) {
         if (stat(PPP_PLUGIN_PATH, &st) != 0) {
             g_set_error_literal(error,
@@ -58,7 +62,7 @@ nm_ppp_manager_create(const char *iface, GError **error)
             return NULL;
         }
 
-        if (!g_module_symbol(plugin, "ppp_ops", (gpointer) &ops)) {
+        if (!g_module_symbol(plugin, "ppp_ops", (gpointer *) &ppp_ops)) {
             g_set_error(error,
                         NM_MANAGER_ERROR,
                         NM_MANAGER_ERROR_MISSING_PLUGIN,
@@ -67,18 +71,21 @@ nm_ppp_manager_create(const char *iface, GError **error)
             return NULL;
         }
 
+        nm_assert(ppp_ops);
+        nm_assert(ppp_ops->create);
+        nm_assert(ppp_ops->start);
+        nm_assert(ppp_ops->stop);
+        nm_assert(ppp_ops->stop_cancel);
+
+        if (!g_atomic_pointer_compare_and_exchange(&_ppp_ops, NULL, ppp_ops)) {
+            g_module_close(plugin);
+            goto again;
+        }
+
         /* after loading glib types from the plugin, we cannot unload the library anymore.
          * Make it resident. */
         g_module_make_resident(plugin);
 
-        nm_assert(ops);
-        nm_assert(ops->create);
-        nm_assert(ops->start);
-        nm_assert(ops->stop);
-        nm_assert(ops->stop_cancel);
-
-        ppp_ops = ops;
-
         nm_log_info(LOGD_CORE | LOGD_PPP, "loaded PPP plugin " PPP_PLUGIN_PATH);
     }
 
@@ -94,6 +101,8 @@ nm_ppp_manager_set_route_parameters(NMPPPManager *self,
                                     guint32       ip6_route_table,
                                     guint32       ip6_route_metric)
 {
+    const NMPPPOps *ppp_ops = ppp_ops_get();
+
     g_return_if_fail(ppp_ops);
 
     ppp_ops->set_route_parameters(self,
@@ -111,6 +120,8 @@ nm_ppp_manager_start(NMPPPManager *self,
                      guint         baud_override,
                      GError **     err)
 {
+    const NMPPPOps *ppp_ops = ppp_ops_get();
+
     g_return_val_if_fail(ppp_ops, FALSE);
 
     return ppp_ops->start(self, req, ppp_name, timeout_secs, baud_override, err);
@@ -122,6 +133,8 @@ nm_ppp_manager_stop(NMPPPManager *           self,
                     NMPPPManagerStopCallback callback,
                     gpointer                 user_data)
 {
+    const NMPPPOps *ppp_ops = ppp_ops_get();
+
     g_return_val_if_fail(ppp_ops, NULL);
 
     return ppp_ops->stop(self, cancellable, callback, user_data);
@@ -130,6 +143,8 @@ nm_ppp_manager_stop(NMPPPManager *           self,
 void
 nm_ppp_manager_stop_cancel(NMPPPManagerStopHandle *handle)
 {
+    const NMPPPOps *ppp_ops = ppp_ops_get();
+
     g_return_if_fail(ppp_ops);
     g_return_if_fail(handle);
 
diff --git a/src/core/ppp/nm-ppp-manager.c b/src/core/ppp/nm-ppp-manager.c
index 396a49ae..fefc5d61 100644
--- a/src/core/ppp/nm-ppp-manager.c
+++ b/src/core/ppp/nm-ppp-manager.c
@@ -28,8 +28,9 @@
 #include <linux/rtnetlink.h>
 
 #include "NetworkManagerUtils.h"
-#include "platform/nm-platform.h"
-#include "nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -998,7 +999,7 @@ _ppp_manager_start(NMPPPManager *self,
 
     /* Make sure /dev/ppp exists (bgo #533064) */
     if (stat("/dev/ppp", &st) || !S_ISCHR(st.st_mode))
-        nm_utils_modprobe(NULL, FALSE, "ppp_generic", NULL);
+        nmp_utils_modprobe(NULL, FALSE, "ppp_generic", NULL);
 
     connection = nm_act_request_get_applied_connection(req);
     g_return_val_if_fail(connection, FALSE);
@@ -1445,11 +1446,11 @@ nm_ppp_manager_class_init(NMPPPManagerClass *manager_class)
                                   NULL,
                                   G_TYPE_NONE,
                                   2,
-                                  G_TYPE_UINT /*guint32 in_bytes*/,
-                                  G_TYPE_UINT /*guint32 out_bytes*/);
+                                  G_TYPE_UINT,  /* guint32 in_bytes */
+                                  G_TYPE_UINT); /* guint32 out_bytes */
 }
 
-NMPPPOps ppp_ops = {
+const NMPPPOps ppp_ops = {
     .create               = _ppp_manager_new,
     .set_route_parameters = _ppp_manager_set_route_parameters,
     .start                = _ppp_manager_start,
diff --git a/src/core/ppp/nm-pppd-plugin.c b/src/core/ppp/nm-pppd-plugin.c
index c9016dac..5ffa7d17 100644
--- a/src/core/ppp/nm-pppd-plugin.c
+++ b/src/core/ppp/nm-pppd-plugin.c
@@ -20,7 +20,7 @@
 #include <pppd/eui64.h>
 #include <pppd/ipv6cp.h>
 
-#include "nm-glib-aux/nm-default-glib.h"
+#include "libnm-glib-aux/nm-default-glib.h"
 
 #include "nm-dbus-interface.h"
 
@@ -226,11 +226,10 @@ nm_ip_up(void *data, int arg)
         if (opts.dnsaddr[1])
             dns[len++] = opts.dnsaddr[1];
 
-        g_variant_builder_add(
-            &builder,
-            "{sv}",
-            NM_PPP_IP4_CONFIG_DNS,
-            g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, dns, len, sizeof(guint32)));
+        g_variant_builder_add(&builder,
+                              "{sv}",
+                              NM_PPP_IP4_CONFIG_DNS,
+                              nm_g_variant_new_au(dns, len));
     }
 
     if (opts.winsaddr[0] || opts.winsaddr[1]) {
@@ -242,11 +241,10 @@ nm_ip_up(void *data, int arg)
         if (opts.winsaddr[1])
             wins[len++] = opts.winsaddr[1];
 
-        g_variant_builder_add(
-            &builder,
-            "{sv}",
-            NM_PPP_IP4_CONFIG_WINS,
-            g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, wins, len, sizeof(guint32)));
+        g_variant_builder_add(&builder,
+                              "{sv}",
+                              NM_PPP_IP4_CONFIG_WINS,
+                              nm_g_variant_new_au(wins, len));
     }
 
     g_message("nm-ppp-plugin: sending IPv4 config to NetworkManager...");
diff --git a/src/core/settings/nm-agent-manager.c b/src/core/settings/nm-agent-manager.c
index 7e617909..a1c2eb9c 100644
--- a/src/core/settings/nm-agent-manager.c
+++ b/src/core/settings/nm-agent-manager.c
@@ -9,7 +9,7 @@
 
 #include <pwd.h>
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-dbus-interface.h"
 #include "nm-secret-agent.h"
 #include "nm-auth-utils.h"
@@ -19,7 +19,7 @@
 #include "nm-session-monitor.h"
 #include "nm-simple-connection.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "c-list/src/c-list.h"
 
 /*****************************************************************************/
diff --git a/src/core/settings/nm-secret-agent.c b/src/core/settings/nm-secret-agent.c
index 7e7c5d7d..45152c27 100644
--- a/src/core/settings/nm-secret-agent.c
+++ b/src/core/settings/nm-secret-agent.c
@@ -10,11 +10,11 @@
 #include <sys/types.h>
 #include <pwd.h>
 
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
 #include "nm-dbus-interface.h"
-#include "nm-core-internal.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-simple-connection.h"
 #include "NetworkManagerUtils.h"
 #include "c-list/src/c-list.h"
@@ -583,7 +583,7 @@ nm_secret_agent_delete_secrets(NMSecretAgent *       self,
     priv = NM_SECRET_AGENT_GET_PRIVATE(self);
 
     /* No secrets sent; agents must be smart enough to track secrets using the UUID or something */
-    dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
+    dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_WITH_NON_SECRET);
 
     call_id = _call_id_new(self, METHOD_DELETE_SECRETS, path, NULL, callback, callback_data);
 
diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c
index 0ff07189..641f3297 100644
--- a/src/core/settings/nm-settings-connection.c
+++ b/src/core/settings/nm-settings-connection.c
@@ -10,8 +10,8 @@
 
 #include "c-list/src/c-list.h"
 
-#include "nm-glib-aux/nm-keyfile-aux.h"
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-glib-aux/nm-keyfile-aux.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-config.h"
 #include "nm-config-data.h"
 #include "nm-dbus-interface.h"
@@ -20,7 +20,7 @@
 #include "nm-auth-utils.h"
 #include "nm-agent-manager.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-audit-manager.h"
 #include "nm-settings.h"
 #include "nm-dbus-manager.h"
@@ -30,10 +30,11 @@
 #define AUTOCONNECT_RETRIES_FOREVER     -1
 #define AUTOCONNECT_RESET_RETRIES_TIMER 300
 
-#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES                           \
-    ((NMSettingsUpdate2Flags)(                                                \
-        NM_SETTINGS_UPDATE2_FLAG_TO_DISK | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \
-        | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))
+#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES                          \
+    ((NMSettingsUpdate2Flags) (NM_SETTINGS_UPDATE2_FLAG_TO_DISK              \
+                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY          \
+                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED \
+                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))
 
 /*****************************************************************************/
 
@@ -79,19 +80,17 @@ typedef struct _NMSettingsConnectionPrivate {
 
     NMConnection *connection;
 
+    struct {
+        NMConnectionSerializationOptions options;
+        GVariant *                       variant;
+    } getsettings_cached;
+
     NMSettingsStorage *storage;
 
     char *filename;
 
     NMDevice *default_wired_device;
 
-    /* Caches secrets from on-disk connections; were they not cached any
-     * call to nm_connection_clear_secrets() wipes them out and we'd have
-     * to re-read them from disk which defeats the purpose of having the
-     * connection in-memory at all.
-     */
-    GVariant *system_secrets;
-
     /* Caches secrets from agents during the activation process; if new system
      * secrets are returned from an agent, they get written out to disk,
      * triggering a re-read of the connection, which reads only system
@@ -168,7 +167,6 @@ static const GDBusSignalInfo             signal_info_updated;
 static const GDBusSignalInfo             signal_info_removed;
 static const NMDBusInterfaceInfoExtended interface_info_settings_connection;
 
-static void update_system_secrets_cache(NMSettingsConnection *self, NMConnection *new);
 static void update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new);
 
 /*****************************************************************************/
@@ -256,6 +254,57 @@ _seen_bssids_hash_new(void)
 
 /*****************************************************************************/
 
+static void
+_getsettings_cached_clear(NMSettingsConnectionPrivate *priv)
+{
+    if (nm_clear_pointer(&priv->getsettings_cached.variant, g_variant_unref)) {
+        priv->getsettings_cached.options.timestamp.has = FALSE;
+        priv->getsettings_cached.options.timestamp.val = 0;
+        nm_clear_g_free((gpointer *) &priv->getsettings_cached.options.seen_bssids);
+    }
+}
+
+static GVariant *
+_getsettings_cached_get(NMSettingsConnection *self, const NMConnectionSerializationOptions *options)
+{
+    NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
+    GVariant *                   variant;
+
+    if (priv->getsettings_cached.variant) {
+        if (nm_connection_serialization_options_equal(&priv->getsettings_cached.options, options)) {
+#if NM_MORE_ASSERTS > 10
+            gs_unref_variant GVariant *variant2 = NULL;
+
+            variant = nm_connection_to_dbus_full(priv->connection,
+                                                 NM_CONNECTION_SERIALIZE_WITH_NON_SECRET,
+                                                 options);
+            nm_assert(variant);
+            variant2 = g_variant_new("(@a{sa{sv}})", variant);
+            nm_assert(g_variant_equal(priv->getsettings_cached.variant, variant2));
+#endif
+            return priv->getsettings_cached.variant;
+        }
+        _getsettings_cached_clear(priv);
+    }
+
+    nm_assert(!priv->getsettings_cached.options.seen_bssids);
+
+    variant = nm_connection_to_dbus_full(priv->connection,
+                                         NM_CONNECTION_SERIALIZE_WITH_NON_SECRET,
+                                         options);
+    nm_assert(variant);
+
+    priv->getsettings_cached.variant = g_variant_ref_sink(g_variant_new("(@a{sa{sv}})", variant));
+
+    priv->getsettings_cached.options = *options;
+    priv->getsettings_cached.options.seen_bssids =
+        nm_utils_strv_dup_packed(priv->getsettings_cached.options.seen_bssids, -1);
+
+    return priv->getsettings_cached.variant;
+}
+
+/*****************************************************************************/
+
 NMConnection *
 nm_settings_connection_get_connection(NMSettingsConnection *self)
 {
@@ -287,6 +336,9 @@ _nm_settings_connection_set_connection(NMSettingsConnection *           self,
         priv->connection = g_object_ref(new_connection);
         nmtst_connection_assert_unchanging(priv->connection);
 
+        _getsettings_cached_clear(priv);
+        _nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(priv->settings);
+
         /* note that we only return @connection_old if the new connection actually differs from
          * before.
          *
@@ -298,11 +350,6 @@ _nm_settings_connection_set_connection(NMSettingsConnection *           self,
         NM_SET_OUT(out_connection_old, g_steal_pointer(&connection_old));
     }
 
-    if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS))
-        update_system_secrets_cache(self, NULL);
-    else if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS))
-        update_system_secrets_cache(self, priv->connection);
-
     if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS))
         update_agent_secrets_cache(self, NULL);
     else if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS))
@@ -434,63 +481,20 @@ nm_settings_connection_check_permission(NMSettingsConnection *self, const char *
 /*****************************************************************************/
 
 static void
-update_system_secrets_cache(NMSettingsConnection *self, NMConnection *new)
-{
-    NMSettingsConnectionPrivate *priv               = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
-    gs_unref_object NMConnection *connection_cloned = NULL;
-    gs_unref_variant GVariant *old_secrets          = NULL;
-
-    old_secrets = g_steal_pointer(&priv->system_secrets);
-
-    if (!new)
-        goto out;
-
-    /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
-     *   in particular if there are no secrets to begin with. */
-
-    connection_cloned = nm_simple_connection_new_clone(new);
-
-    /* Clear out non-system-owned and not-saved secrets */
-    _nm_connection_clear_secrets_by_secret_flags(connection_cloned, NM_SETTING_SECRET_FLAG_NONE);
-
-    priv->system_secrets = nm_g_variant_ref_sink(
-        nm_connection_to_dbus(connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
-
-out:
-    if (_LOGT_ENABLED()) {
-        if ((!!old_secrets) != (!!priv->system_secrets)) {
-            _LOGT("update system secrets: secrets %s", old_secrets ? "cleared" : "set");
-        } else if (priv->system_secrets && !g_variant_equal(old_secrets, priv->system_secrets))
-            _LOGT("update system secrets: secrets updated");
-    }
-}
-
-static void
 update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new)
 {
-    NMSettingsConnectionPrivate *priv               = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
-    gs_unref_object NMConnection *connection_cloned = NULL;
-    gs_unref_variant GVariant *old_secrets          = NULL;
+    NMSettingsConnectionPrivate *priv      = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
+    gs_unref_variant GVariant *old_secrets = NULL;
 
     old_secrets = g_steal_pointer(&priv->agent_secrets);
 
-    if (!new)
-        goto out;
-
-    /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
-     *   in particular if there are no secrets to begin with. */
-
-    connection_cloned = nm_simple_connection_new_clone(new);
-
-    /* Clear out non-system-owned secrets */
-    _nm_connection_clear_secrets_by_secret_flags(connection_cloned,
-                                                 NM_SETTING_SECRET_FLAG_NOT_SAVED
-                                                     | NM_SETTING_SECRET_FLAG_AGENT_OWNED);
-
-    priv->agent_secrets = nm_g_variant_ref_sink(
-        nm_connection_to_dbus(connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
+    if (new) {
+        priv->agent_secrets = nm_g_variant_ref_sink(
+            nm_connection_to_dbus(new,
+                                  NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED
+                                      | NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED));
+    }
 
-out:
     if (_LOGT_ENABLED()) {
         if ((!!old_secrets) != (!!priv->agent_secrets)) {
             _LOGT("update agent secrets: secrets %s", old_secrets ? "cleared" : "set");
@@ -499,39 +503,6 @@ out:
     }
 }
 
-void
-nm_settings_connection_clear_secrets(NMSettingsConnection *self,
-                                     gboolean              clear_cached_system_secrets,
-                                     gboolean              persist)
-{
-    gs_unref_object NMConnection *connection_cloned = NULL;
-
-    if (!nm_settings_connection_still_valid(self))
-        return;
-
-    /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */
-
-    connection_cloned = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
-
-    nm_connection_clear_secrets(connection_cloned);
-
-    if (!nm_settings_connection_update(
-            self,
-            connection_cloned,
-            persist ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
-                    : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
-            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
-            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
-            NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
-                | (clear_cached_system_secrets
-                       ? NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS
-                       : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)
-                | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS,
-            "clear-secrets",
-            NULL))
-        nm_assert_not_reached();
-}
-
 static gboolean
 _secrets_update(NMConnection * connection,
                 const char *   setting_name,
@@ -829,6 +800,18 @@ nm_settings_connection_new_secrets(NMSettingsConnection *self,
     return TRUE;
 }
 
+static gboolean
+match_secret_by_setting_name_and_flags_cb(NMSetting *          setting,
+                                          const char *         secret,
+                                          NMSettingSecretFlags flags,
+                                          gpointer             user_data)
+{
+    const char *get_secrets_setting_name = user_data;
+
+    return nm_streq(nm_setting_get_name(setting), get_secrets_setting_name)
+           && NM_FLAGS_HAS(flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED);
+}
+
 static void
 get_secrets_done_cb(NMAgentManager *             manager,
                     NMAgentManagerCallId         call_id_a,
@@ -846,7 +829,6 @@ get_secrets_done_cb(NMAgentManager *             manager,
     NMSettingsConnectionPrivate *priv;
     NMConnection *               applied_connection;
     gs_free_error GError *local                    = NULL;
-    gs_unref_variant GVariant *system_secrets      = NULL;
     gs_unref_object NMConnection *new_connection   = NULL;
     gboolean                      agent_had_system = FALSE;
     ForEachSecretFlags cmp_flags = {NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE};
@@ -917,18 +899,12 @@ get_secrets_done_cb(NMAgentManager *             manager,
 
     _LOGD("(%s:%p) secrets request completed", setting_name, call_id);
 
-    system_secrets = nm_g_variant_ref(priv->system_secrets);
-
     new_connection = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
 
-    nm_connection_clear_secrets(new_connection);
-
-    if (!_secrets_update(new_connection, setting_name, system_secrets, NULL, &local)) {
-        _LOGD("(%s:%p) failed to update with existing secrets: %s",
-              setting_name,
-              call_id,
-              local->message);
-    }
+    /* Remove old agent-owned secrets in the requested setting */
+    nm_connection_clear_secrets_with_flags(new_connection,
+                                           match_secret_by_setting_name_and_flags_cb,
+                                           (gpointer) setting_name);
 
     /* Update the connection with the agent's secrets; by this point if any
      * system-owned secrets exist in 'secrets' the agent that provided them
@@ -962,7 +938,8 @@ get_secrets_done_cb(NMAgentManager *             manager,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
             NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
-                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+                | (agent_had_system ? NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+                                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)
                 | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
             "get-new-secrets",
             NULL))
@@ -970,6 +947,8 @@ get_secrets_done_cb(NMAgentManager *             manager,
 
     applied_connection = call_id->applied_connection;
     if (applied_connection) {
+        gs_unref_variant GVariant *filtered_secrets2 = NULL;
+
         get_cmp_flags(self,
                       call_id,
                       applied_connection,
@@ -981,18 +960,12 @@ get_secrets_done_cb(NMAgentManager *             manager,
                       &agent_had_system,
                       &cmp_flags);
 
-        nm_connection_clear_secrets(applied_connection);
+        nm_connection_clear_secrets_with_flags(applied_connection,
+                                               match_secret_by_setting_name_and_flags_cb,
+                                               (gpointer) setting_name);
 
-        if (!system_secrets
-            || nm_connection_update_secrets(applied_connection,
-                                            setting_name,
-                                            system_secrets,
-                                            NULL)) {
-            gs_unref_variant GVariant *filtered_secrets2 = NULL;
-
-            filtered_secrets2 = validate_secret_flags(applied_connection, secrets, &cmp_flags);
-            nm_connection_update_secrets(applied_connection, setting_name, filtered_secrets2, NULL);
-        }
+        filtered_secrets2 = validate_secret_flags(applied_connection, secrets, &cmp_flags);
+        nm_connection_update_secrets(applied_connection, setting_name, filtered_secrets2, NULL);
     }
 
     _get_secrets_info_callback(call_id, agent_username, setting_name, local);
@@ -1060,7 +1033,8 @@ nm_settings_connection_get_secrets(NMSettingsConnection *          self,
     NMAgentManagerCallId         call_id_a;
     gs_free char *               joined_hints = NULL;
     NMSettingsConnectionCallId * call_id;
-    GError *                     local = NULL;
+    GError *                     local        = NULL;
+    gs_unref_variant GVariant *system_secrets = NULL;
 
     g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), NULL);
     g_return_val_if_fail(
@@ -1111,14 +1085,15 @@ nm_settings_connection_get_secrets(NMSettingsConnection *          self,
      * Then we know that the this request probably did not yet include the latest secret-agent. */
     priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id(priv->agent_mgr);
 
-    /* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets()
-     * will clear secrets on this object's settings.
-     */
+    system_secrets = nm_g_variant_ref_sink(
+        nm_connection_to_dbus(nm_settings_connection_get_connection(self),
+                              NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED));
+
     call_id_a = nm_agent_manager_get_secrets(priv->agent_mgr,
                                              nm_dbus_object_get_path(NM_DBUS_OBJECT(self)),
                                              nm_settings_connection_get_connection(self),
                                              subject,
-                                             priv->system_secrets,
+                                             system_secrets,
                                              setting_name,
                                              flags,
                                              hints,
@@ -1327,7 +1302,6 @@ get_settings_auth_cb(NMSettingsConnection * self,
 {
     gs_free const char **            seen_bssids = NULL;
     NMConnectionSerializationOptions options     = {};
-    GVariant *                       settings;
 
     if (error) {
         g_dbus_method_invocation_return_gerror(context, error);
@@ -1354,10 +1328,8 @@ get_settings_auth_cb(NMSettingsConnection * self,
      * get returned by the GetSecrets method which can be better
      * protected against leakage of secrets to unprivileged callers.
      */
-    settings = nm_connection_to_dbus_full(nm_settings_connection_get_connection(self),
-                                          NM_CONNECTION_SERIALIZE_NO_SECRETS,
-                                          &options);
-    g_dbus_method_invocation_return_value(context, g_variant_new("(@a{sa{sv}})", settings));
+
+    g_dbus_method_invocation_return_value(context, _getsettings_cached_get(self, &options));
 }
 
 static void
@@ -1488,14 +1460,21 @@ update_auth_cb(NMSettingsConnection * self,
         if (!_nm_connection_aggregate(info->new_settings,
                                       NM_CONNECTION_AGGREGATE_ANY_SECRETS,
                                       NULL)) {
+            gs_unref_variant GVariant *secrets = NULL;
+
             /* If the new connection has no secrets, we do not want to remove all
              * secrets, rather we keep all the existing ones. Do that by merging
              * them in to the new connection.
              */
+            secrets = nm_g_variant_ref_sink(
+                nm_connection_to_dbus(nm_settings_connection_get_connection(self),
+                                      NM_CONNECTION_SERIALIZE_WITH_SECRETS));
+
+            if (secrets)
+                nm_connection_update_secrets(info->new_settings, NULL, secrets, NULL);
+
             if (priv->agent_secrets)
                 nm_connection_update_secrets(info->new_settings, NULL, priv->agent_secrets, NULL);
-            if (priv->system_secrets)
-                nm_connection_update_secrets(info->new_settings, NULL, priv->system_secrets, NULL);
         } else {
             /* Cache the new secrets from the agent, as stuff like inotify-triggered
              * changes to connection's backing config files will blow them away if
@@ -1558,6 +1537,7 @@ update_auth_cb(NMSettingsConnection * self,
                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL)
             | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
             | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET
             | (NM_FLAGS_HAS(info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)
                    ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT
                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE),
@@ -1568,7 +1548,7 @@ update_auth_cb(NMSettingsConnection * self,
         gs_unref_object NMConnection *for_agent = NULL;
 
         /* Dupe the connection so we can clear out non-agent-owned secrets,
-         * as agent-owned secrets are the only ones we send back be saved.
+         * as agent-owned secrets are the only ones we send back to be saved.
          * Only send secrets to agents of the same UID that called update too.
          */
         for_agent = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
@@ -1754,10 +1734,10 @@ impl_settings_connection_update2(NMDBusObject *                     obj,
     g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
 
     if (NM_FLAGS_ANY(flags_u,
-                     ~((guint32)(_NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES
-                                 | NM_SETTINGS_UPDATE2_FLAG_VOLATILE
-                                 | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT
-                                 | NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) {
+                     ~((guint32) (_NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES
+                                  | NM_SETTINGS_UPDATE2_FLAG_VOLATILE
+                                  | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT
+                                  | NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) {
         error = g_error_new_literal(NM_SETTINGS_ERROR,
                                     NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
                                     "Unknown flags");
@@ -1888,9 +1868,9 @@ dbus_get_agent_secrets_cb(NMSettingsConnection *      self,
          * by the time we get here.
          */
         dict = nm_connection_to_dbus(nm_settings_connection_get_connection(self),
-                                     NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+                                     NM_CONNECTION_SERIALIZE_WITH_SECRETS);
         if (!dict)
-            dict = g_variant_new_array(G_VARIANT_TYPE("{sa{sv}}"), NULL, 0);
+            dict = nm_g_variant_singleton_aLsaLsvII();
         g_dbus_method_invocation_return_value(context, g_variant_new("(@a{sa{sv}})", dict));
     }
 }
@@ -1959,8 +1939,9 @@ dbus_clear_secrets_auth_cb(NMSettingsConnection * self,
                            GError *               error,
                            gpointer               user_data)
 {
-    NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
-    gs_free_error GError *local       = NULL;
+    NMSettingsConnectionPrivate *priv               = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
+    gs_free_error GError *local                     = NULL;
+    gs_unref_object NMConnection *connection_cloned = NULL;
 
     if (error) {
         g_dbus_method_invocation_return_gerror(context, error);
@@ -1973,7 +1954,24 @@ dbus_clear_secrets_auth_cb(NMSettingsConnection * self,
         return;
     }
 
-    nm_settings_connection_clear_secrets(self, TRUE, TRUE);
+    /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */
+
+    connection_cloned = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
+
+    nm_connection_clear_secrets(connection_cloned);
+
+    if (!nm_settings_connection_update(
+            self,
+            connection_cloned,
+            NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+            NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS,
+            "clear-secrets",
+            NULL))
+        nm_assert_not_reached();
 
     /* Tell agents to remove secrets for this connection */
     nm_agent_manager_delete_secrets(priv->agent_mgr,
@@ -2142,7 +2140,9 @@ _cmp_last_resort(NMSettingsConnection *a, NMSettingsConnection *b)
 
     /* hm, same UUID. Use their pointer value to give them a stable
      * order. */
-    return (a > b) ? -1 : 1;
+    NM_CMP_DIRECT_PTR(a, b);
+
+    return nm_assert_unreachable_val(0);
 }
 
 /* sorting for "best" connections.
@@ -2184,6 +2184,15 @@ nm_settings_connection_cmp_autoconnect_priority(NMSettingsConnection *a, NMSetti
 }
 
 int
+nm_settings_connection_cmp_autoconnect_priority_with_data(gconstpointer pa,
+                                                          gconstpointer pb,
+                                                          gpointer      user_data)
+{
+    return nm_settings_connection_cmp_autoconnect_priority((NMSettingsConnection *) pa,
+                                                           (NMSettingsConnection *) pb);
+}
+
+int
 nm_settings_connection_cmp_autoconnect_priority_p_with_data(gconstpointer pa,
                                                             gconstpointer pb,
                                                             gpointer      user_data)
@@ -2243,6 +2252,8 @@ nm_settings_connection_update_timestamp(NMSettingsConnection *self, guint64 time
 
     _LOGT("timestamp: set timestamp %" G_GUINT64_FORMAT, timestamp);
 
+    _nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(priv->settings);
+
     if (!priv->kf_db_timestamps)
         return;
 
@@ -2659,7 +2670,6 @@ dispose(GObject *object)
             _get_secrets_cancel(self, call_id, TRUE);
     }
 
-    nm_clear_pointer(&priv->system_secrets, g_variant_unref);
     nm_clear_pointer(&priv->agent_secrets, g_variant_unref);
 
     nm_clear_pointer(&priv->seen_bssids, g_hash_table_destroy);
@@ -2668,6 +2678,8 @@ dispose(GObject *object)
 
     g_clear_object(&priv->connection);
 
+    _getsettings_cached_clear(priv);
+
     nm_clear_pointer(&priv->kf_db_timestamps, nm_key_file_db_unref);
     nm_clear_pointer(&priv->kf_db_seen_bssids, nm_key_file_db_unref);
 
@@ -2732,20 +2744,17 @@ static const NMDBusInterfaceInfoExtended interface_info_settings_connection = {
                     .out_args =
                         NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("result", "a{sv}"), ), ),
                 .handle = impl_settings_connection_update2, ), ),
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_updated,
-                                                &signal_info_removed, ),
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_updated, &signal_info_removed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Unsaved",
-                                                             "b",
-                                                             NM_SETTINGS_CONNECTION_UNSAVED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Unsaved",
+                                                           "b",
+                                                           NM_SETTINGS_CONNECTION_UNSAVED),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Flags",
                                                            "u",
                                                            NM_SETTINGS_CONNECTION_FLAGS),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Filename",
                                                            "s",
                                                            NM_SETTINGS_CONNECTION_FILENAME), ), ),
-    .legacy_property_changed = TRUE,
 };
 
 static void
diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h
index 7c61dbeb..83a6a7f6 100644
--- a/src/core/settings/nm-settings-connection.h
+++ b/src/core/settings/nm-settings-connection.h
@@ -63,6 +63,9 @@ typedef enum {
 
     NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT = (1u << 8),
 
+    /* Is anything other than secrets changing */
+    NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET = (1u << 9),
+
 } NMSettingsConnectionUpdateReason;
 
 typedef enum {
@@ -274,10 +277,6 @@ nm_settings_connection_get_secrets(NMSettingsConnection *          self,
 void nm_settings_connection_cancel_secrets(NMSettingsConnection *      self,
                                            NMSettingsConnectionCallId *call_id);
 
-void nm_settings_connection_clear_secrets(NMSettingsConnection *self,
-                                          gboolean              clear_cached_system_secrets,
-                                          gboolean              persist);
-
 gboolean nm_settings_connection_check_visibility(NMSettingsConnection *self,
                                                  NMSessionMonitor *    session_monitor);
 
@@ -324,6 +323,9 @@ int nm_settings_connection_cmp_timestamp_p_with_data(gconstpointer pa,
                                                      gpointer      user_data);
 int nm_settings_connection_cmp_autoconnect_priority(NMSettingsConnection *a,
                                                     NMSettingsConnection *b);
+int nm_settings_connection_cmp_autoconnect_priority_with_data(gconstpointer pa,
+                                                              gconstpointer pb,
+                                                              gpointer      user_data);
 int nm_settings_connection_cmp_autoconnect_priority_p_with_data(gconstpointer pa,
                                                                 gconstpointer pb,
                                                                 gpointer      user_data);
diff --git a/src/core/settings/nm-settings-plugin.c b/src/core/settings/nm-settings-plugin.c
index 9e81caab..66cc19b6 100644
--- a/src/core/settings/nm-settings-plugin.c
+++ b/src/core/settings/nm-settings-plugin.c
@@ -9,7 +9,7 @@
 #include "nm-settings-plugin.h"
 
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "nm-settings-connection.h"
 
diff --git a/src/core/settings/nm-settings-storage.c b/src/core/settings/nm-settings-storage.c
index c56ba075..e284b822 100644
--- a/src/core/settings/nm-settings-storage.c
+++ b/src/core/settings/nm-settings-storage.c
@@ -7,6 +7,7 @@
 
 #include "nm-settings-storage.h"
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "nm-utils.h"
 #include "nm-settings-plugin.h"
 
@@ -72,7 +73,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     case PROP_UUID:
         /* construct-only */
         self->_uuid = g_value_dup_string(value);
-        nm_assert(!self->_uuid || nm_utils_is_uuid(self->_uuid));
+        nm_assert(!self->_uuid || nm_uuid_is_normalized(self->_uuid));
         break;
     case PROP_FILENAME:
         /* construct-only */
@@ -97,7 +98,7 @@ NMSettingsStorage *
 nm_settings_storage_new(NMSettingsPlugin *plugin, const char *uuid, const char *filename)
 {
     nm_assert(NM_IS_SETTINGS_PLUGIN(plugin));
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     return g_object_new(NM_TYPE_SETTINGS_STORAGE,
                         NM_SETTINGS_STORAGE_PLUGIN,
diff --git a/src/core/settings/nm-settings-storage.h b/src/core/settings/nm-settings-storage.h
index 970cfd77..2e5e1c55 100644
--- a/src/core/settings/nm-settings-storage.h
+++ b/src/core/settings/nm-settings-storage.h
@@ -48,36 +48,35 @@ GType nm_settings_storage_get_type(void);
 NMSettingsStorage *
 nm_settings_storage_new(struct _NMSettingsPlugin *plugin, const char *uuid, const char *filename);
 
+/* forward declare so we don't have to include "nm-settings-plugin.h" here. */
+GType nm_settings_plugin_get_type(void);
+
 static inline struct _NMSettingsPlugin *
 nm_settings_storage_get_plugin(const NMSettingsStorage *self)
 {
-    GType nm_settings_plugin_get_type(void);
-
     g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
 
     nm_assert(G_TYPE_CHECK_INSTANCE_TYPE(self->_plugin, nm_settings_plugin_get_type()));
     return self->_plugin;
 }
 
+gboolean nm_uuid_is_normalized_full(const char *str);
+
 static inline const char *
 nm_settings_storage_get_uuid(const NMSettingsStorage *self)
 {
-    gboolean nm_utils_is_uuid(const char *str);
-
     g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
 
-    nm_assert(nm_utils_is_uuid(self->_uuid));
+    nm_assert(nm_uuid_is_normalized_full(self->_uuid));
     return self->_uuid;
 }
 
 static inline const char *
 nm_settings_storage_get_uuid_opt(const NMSettingsStorage *self)
 {
-    gboolean nm_utils_is_uuid(const char *str);
-
     g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
 
-    nm_assert(!self->_uuid || nm_utils_is_uuid(self->_uuid));
+    nm_assert(!self->_uuid || nm_uuid_is_normalized_full(self->_uuid));
     return self->_uuid;
 }
 
diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c
index 3d31fb8b..c876ea14 100644
--- a/src/core/settings/nm-settings.c
+++ b/src/core/settings/nm-settings.c
@@ -20,9 +20,10 @@
     #include <selinux/selinux.h>
 #endif
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
-#include "nm-glib-aux/nm-keyfile-aux.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-keyfile-aux.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
 #include "nm-setting-8021x.h"
@@ -44,17 +45,17 @@
 #include "nm-setting-proxy.h"
 #include "nm-setting-bond.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
-#include "nm-std-aux/c-list-util.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-std-aux/c-list-util.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-dbus-object.h"
 #include "devices/nm-device-ethernet.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-plugin.h"
 #include "nm-dbus-manager.h"
 #include "nm-auth-utils.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-session-monitor.h"
 #include "plugins/keyfile/nms-keyfile-plugin.h"
 #include "plugins/keyfile/nms-keyfile-storage.h"
@@ -139,7 +140,7 @@ nm_assert_storage_data_lst(CList *head)
         u = nm_settings_storage_get_uuid(sd->storage);
         if (!uuid) {
             uuid = u;
-            nm_assert(nm_utils_is_uuid(uuid));
+            nm_assert(nm_uuid_is_normalized(uuid));
         } else
             nm_assert(nm_streq0(uuid, u));
     }
@@ -182,7 +183,7 @@ _sett_conn_entry_new(const char *uuid)
     SettConnEntry *sett_conn_entry;
     gsize          l_p_1;
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     l_p_1 = strlen(uuid) + 1;
 
@@ -370,6 +371,7 @@ typedef struct {
     CList connections_lst_head;
 
     NMSettingsConnection **connections_cached_list;
+    NMSettingsConnection **connections_cached_list_sorted_by_autoconnect_priority;
 
     GSList *unmanaged_specs;
     GSList *unrecognized_specs;
@@ -388,6 +390,12 @@ typedef struct {
 
     bool started : 1;
 
+    /* Whether NMSettingsConnections changed in a way that affects the comparison
+     * with nm_settings_connection_cmp_autoconnect_priority_with_data(). In that case,
+     * we may need to re-sort the connections_cached_list_sorted_by_autoconnect_priority
+     * list. */
+    bool sorted_by_autoconnect_priority_maybe_changed : 1;
+
 } NMSettingsPrivate;
 
 struct _NMSettings {
@@ -1433,7 +1441,8 @@ _plugin_connections_reload(NMSettings *self)
         NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
         TRUE,
         NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET);
 
     for (iter = priv->plugins; iter; iter = iter->next)
         nm_settings_plugin_load_connections_done(iter->data);
@@ -1460,7 +1469,7 @@ _add_connection_to_first_plugin(NMSettings *                 self,
 
     uuid = nm_connection_get_uuid(new_connection);
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     for (iter = priv->plugins; iter; iter = iter->next) {
         NMSettingsPlugin *plugin                               = NM_SETTINGS_PLUGIN(iter->data);
@@ -1541,9 +1550,7 @@ _add_connection_to_first_plugin(NMSettings *                 self,
         }
 
         agent_owned_secrets =
-            nm_connection_to_dbus(new_connection,
-                                  NM_CONNECTION_SERIALIZE_ONLY_SECRETS
-                                      | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+            nm_connection_to_dbus(new_connection, NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
         connection_to_add_real =
             _connection_changed_normalize_connection(storage,
                                                      connection_to_add,
@@ -1912,7 +1919,8 @@ again_delete_tombstone:
         _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK,
         FALSE,
         NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET
             | (NM_FLAGS_HAS(add_reason, NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT)
                    ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT
                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE));
@@ -2218,9 +2226,7 @@ nm_settings_update_connection(NMSettings *                     self,
             nm_assert(nm_streq(uuid, nm_settings_storage_get_uuid(new_storage)));
 
             agent_owned_secrets =
-                nm_connection_to_dbus(connection,
-                                      NM_CONNECTION_SERIALIZE_ONLY_SECRETS
-                                          | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+                nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
             new_connection_real = _connection_changed_normalize_connection(new_storage,
                                                                            new_connection,
                                                                            agent_owned_secrets,
@@ -2297,7 +2303,7 @@ nm_settings_delete_connection(NMSettings *          self,
     nm_assert(NM_IS_SETTINGS_STORAGE(cur_storage));
 
     uuid = nm_settings_storage_get_uuid(cur_storage);
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     sett_conn_entry = _sett_conn_entries_get(self, uuid);
 
@@ -2691,9 +2697,9 @@ impl_settings_add_connection2(NMDBusObject *                     obj,
     g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
 
     if (NM_FLAGS_ANY(flags_u,
-                     ~((guint32)(NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK
-                                 | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY
-                                 | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) {
+                     ~((guint32) (NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK
+                                  | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY
+                                  | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) {
         g_dbus_method_invocation_take_error(invocation,
                                             g_error_new_literal(NM_SETTINGS_ERROR,
                                                                 NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
@@ -2814,7 +2820,8 @@ impl_settings_load_connections(NMDBusObject *                     obj,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
             TRUE,
             NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET);
 
         for (iter = priv->plugins; iter; iter = iter->next)
             nm_settings_plugin_load_connections_done(iter->data);
@@ -2832,7 +2839,7 @@ impl_settings_load_connections(NMDBusObject *                     obj,
 
     g_dbus_method_invocation_return_value(invocation,
                                           g_variant_new("(b^as)",
-                                                        (gboolean)(!failures),
+                                                        (gboolean) (!failures),
                                                         failures
                                                             ? (const char **) failures->pdata
                                                             : NM_PTRARRAY_EMPTY(const char *)));
@@ -2870,25 +2877,48 @@ impl_settings_reload_connections(NMDBusObject *                     obj,
 
 /*****************************************************************************/
 
+void
+_nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(NMSettings *self)
+{
+    NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
+
+    priv->sorted_by_autoconnect_priority_maybe_changed = TRUE;
+}
+
 static void
 _clear_connections_cached_list(NMSettingsPrivate *priv)
 {
-    if (!priv->connections_cached_list)
-        return;
+    if (priv->connections_cached_list) {
+        nm_assert(priv->connections_len == NM_PTRARRAY_LEN(priv->connections_cached_list));
 
-    nm_assert(priv->connections_len == NM_PTRARRAY_LEN(priv->connections_cached_list));
+#if NM_MORE_ASSERTS
+        /* set the pointer to a bogus value. This makes it more apparent
+         * if somebody has a reference to the cached list and still uses
+         * it. That is a bug, this code just tries to make it blow up
+         * more eagerly. */
+        memset(priv->connections_cached_list,
+               0x43,
+               sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
+#endif
+
+        nm_clear_g_free(&priv->connections_cached_list);
+    }
+    if (priv->connections_cached_list_sorted_by_autoconnect_priority) {
+        nm_assert(priv->connections_len
+                  == NM_PTRARRAY_LEN(priv->connections_cached_list_sorted_by_autoconnect_priority));
 
 #if NM_MORE_ASSERTS
-    /* set the pointer to a bogus value. This makes it more apparent
-     * if somebody has a reference to the cached list and still uses
-     * it. That is a bug, this code just tries to make it blow up
-     * more eagerly. */
-    memset(priv->connections_cached_list,
-           0x43,
-           sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
+        /* set the pointer to a bogus value. This makes it more apparent
+         * if somebody has a reference to the cached list and still uses
+         * it. That is a bug, this code just tries to make it blow up
+         * more eagerly. */
+        memset(priv->connections_cached_list_sorted_by_autoconnect_priority,
+               0x42,
+               sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
 #endif
 
-    nm_clear_g_free(&priv->connections_cached_list);
+        nm_clear_g_free(&priv->connections_cached_list_sorted_by_autoconnect_priority);
+    }
 }
 
 static void
@@ -3027,6 +3057,65 @@ nm_settings_get_connections(NMSettings *self, guint *out_len)
     return priv->connections_cached_list;
 }
 
+NMSettingsConnection *const *
+nm_settings_get_connections_sorted_by_autoconnect_priority(NMSettings *self, guint *out_len)
+{
+    NMSettingsPrivate *priv;
+    gboolean           needs_sort = FALSE;
+
+    g_return_val_if_fail(NM_IS_SETTINGS(self), NULL);
+
+    priv = NM_SETTINGS_GET_PRIVATE(self);
+
+    nm_assert(priv->connections_len == c_list_length(&priv->connections_lst_head));
+    nm_assert(
+        !priv->connections_cached_list_sorted_by_autoconnect_priority
+        || (priv->connections_len
+            == NM_PTRARRAY_LEN(priv->connections_cached_list_sorted_by_autoconnect_priority)));
+
+    if (!priv->connections_cached_list_sorted_by_autoconnect_priority) {
+        NMSettingsConnection *const *list_cached;
+        guint                        len;
+
+        list_cached = nm_settings_get_connections(self, &len);
+        priv->connections_cached_list_sorted_by_autoconnect_priority =
+            nm_memdup(list_cached, sizeof(NMSettingsConnection *) * (len + 1));
+        needs_sort = (len > 1);
+    } else if (priv->sorted_by_autoconnect_priority_maybe_changed) {
+        if (!nm_utils_ptrarray_is_sorted(
+                (gconstpointer *) priv->connections_cached_list_sorted_by_autoconnect_priority,
+                priv->connections_len,
+                FALSE,
+                nm_settings_connection_cmp_autoconnect_priority_with_data,
+                NULL)) {
+            /* We cache the sorted list, but we don't monitor all entries whether they
+             * get modified to invalidate the sort order. So every time we have to check
+             * whether the sort order is still correct. The vast majority of the time it
+             * is, and this check is faster than sorting anew. */
+            needs_sort = TRUE;
+        }
+    } else {
+        nm_assert(nm_utils_ptrarray_is_sorted(
+            (gconstpointer *) priv->connections_cached_list_sorted_by_autoconnect_priority,
+            priv->connections_len,
+            TRUE,
+            nm_settings_connection_cmp_autoconnect_priority_with_data,
+            NULL));
+    }
+
+    priv->sorted_by_autoconnect_priority_maybe_changed = FALSE;
+    if (needs_sort) {
+        g_qsort_with_data(priv->connections_cached_list_sorted_by_autoconnect_priority,
+                          priv->connections_len,
+                          sizeof(NMSettingsConnection *),
+                          nm_settings_connection_cmp_autoconnect_priority_p_with_data,
+                          NULL);
+    }
+
+    NM_SET_OUT(out_len, priv->connections_len);
+    return priv->connections_cached_list_sorted_by_autoconnect_priority;
+}
+
 /**
  * nm_settings_get_connections_clone:
  * @self: the #NMSetting
@@ -3058,9 +3147,13 @@ nm_settings_get_connections_clone(NMSettings *                   self,
 
     g_return_val_if_fail(NM_IS_SETTINGS(self), NULL);
 
-    list_cached = nm_settings_get_connections(self, &len);
+    if (sort_compare_func == nm_settings_connection_cmp_autoconnect_priority_p_with_data) {
+        list_cached       = nm_settings_get_connections_sorted_by_autoconnect_priority(self, &len);
+        sort_compare_func = NULL;
+    } else
+        list_cached = nm_settings_get_connections(self, &len);
 
-#if NM_MORE_ASSERTS
+#if NM_MORE_ASSERTS > 10
     nm_assert(list_cached);
     for (i = 0; i < len; i++)
         nm_assert(NM_IS_SETTINGS_CONNECTION(list_cached[i]));
@@ -3204,7 +3297,7 @@ add_plugin_load_file(NMSettings *self, const char *pname, GError **error)
 
     /* errors after this point are fatal, because we loaded the shared library already. */
 
-    if (!g_module_symbol(module, "nm_settings_plugin_factory", (gpointer)(&factory_func))) {
+    if (!g_module_symbol(module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_FAILED,
@@ -4027,18 +4120,16 @@ static const NMDBusInterfaceInfoExtended interface_info_settings = {
                     .in_args =
                         NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("hostname", "s"), ), ),
                 .handle = impl_settings_save_hostname, ), ),
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_new_connection,
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_new_connection,
                                                 &signal_info_connection_removed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Connections",
-                                                             "ao",
-                                                             NM_SETTINGS_CONNECTIONS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Hostname", "s", NM_SETTINGS_HOSTNAME),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("CanModify",
-                                                             "b",
-                                                             NM_SETTINGS_CAN_MODIFY), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Connections",
+                                                           "ao",
+                                                           NM_SETTINGS_CONNECTIONS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Hostname", "s", NM_SETTINGS_HOSTNAME),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("CanModify",
+                                                           "b",
+                                                           NM_SETTINGS_CAN_MODIFY), ), ),
 };
 
 static void
diff --git a/src/core/settings/nm-settings.h b/src/core/settings/nm-settings.h
index 09a0af57..ce18fecc 100644
--- a/src/core/settings/nm-settings.h
+++ b/src/core/settings/nm-settings.h
@@ -79,6 +79,9 @@ void nm_settings_add_connection_dbus(NMSettings *                    self,
 
 NMSettingsConnection *const *nm_settings_get_connections(NMSettings *settings, guint *out_len);
 
+NMSettingsConnection *const *
+nm_settings_get_connections_sorted_by_autoconnect_priority(NMSettings *self, guint *out_len);
+
 NMSettingsConnection **nm_settings_get_connections_clone(NMSettings *                   self,
                                                          guint *                        out_len,
                                                          NMSettingsConnectionFilterFunc func,
@@ -127,4 +130,6 @@ const char *nm_settings_get_startup_complete_blocked_reason(NMSettings *self,
 
 void nm_settings_kf_db_write(NMSettings *settings);
 
+void _nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(NMSettings *self);
+
 #endif /* __NM_SETTINGS_H__ */
diff --git a/src/core/settings/plugins/ifcfg-rh/meson.build b/src/core/settings/plugins/ifcfg-rh/meson.build
index a2f11a92..c62fa89d 100644
--- a/src/core/settings/plugins/ifcfg-rh/meson.build
+++ b/src/core/settings/plugins/ifcfg-rh/meson.build
@@ -28,7 +28,6 @@ libnms_ifcfg_rh_core = static_library(
     'shvar.c',
   ),
   dependencies: core_default_dep,
-  c_args: daemon_c_flags,
 )
 
 libnm_settings_plugin_ifcfg_rh = shared_module(
@@ -38,7 +37,6 @@ libnm_settings_plugin_ifcfg_rh = shared_module(
     'nms-ifcfg-rh-plugin.c',
   ),
   dependencies: core_plugin_dep,
-  c_args: daemon_c_flags,
   link_with: libnms_ifcfg_rh_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index d114db0f..5038ac6a 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -13,12 +13,12 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "nm-std-aux/c-list-util.h"
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/c-list-util.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-config.h"
 #include "nm-dbus-manager.h"
 #include "settings/nm-settings-plugin.h"
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 209957d9..24d67003 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -18,7 +18,8 @@
 #include <linux/rtnetlink.h>
 #include <linux/if_ether.h>
 
-#include "nm-glib-aux/nm-secret-utils.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-secret-utils.h"
 #include "nm-connection.h"
 #include "nm-dbus-interface.h"
 #include "nm-setting-connection.h"
@@ -38,11 +39,11 @@
 #include "nm-setting-user.h"
 #include "nm-setting-proxy.h"
 #include "nm-setting-generic.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-utils.h"
-#include "nm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-base.h"
 
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "NetworkManagerUtils.h"
 
 #include "nms-ifcfg-rh-common.h"
@@ -405,11 +406,9 @@ make_connection_setting(const char *file,
     /* Try for a UUID key before falling back to hashing the file name */
     uuid = svGetValueStr(ifcfg, "UUID", &uuid_free);
     if (!uuid) {
-        uuid_free = nm_utils_uuid_generate_from_string(svFileGetName(ifcfg),
-                                                       -1,
-                                                       NM_UTILS_UUID_TYPE_LEGACY,
-                                                       NULL);
-        uuid      = uuid_free;
+        uuid_free =
+            nm_uuid_generate_from_string_str(svFileGetName(ifcfg), -1, NM_UUID_TYPE_LEGACY, NULL);
+        uuid = uuid_free;
     }
 
     g_object_set(s_con,
@@ -2707,7 +2706,8 @@ make_tc_setting(shvarFile *ifcfg)
     }
 
     if (nm_setting_tc_config_get_num_qdiscs(s_tc) > 0
-        || nm_setting_tc_config_get_num_tfilters(s_tc) > 0)
+        || nm_setting_tc_config_get_num_tfilters(s_tc) > 0
+        || svGetValueBoolean(ifcfg, "TC_COMMIT", FALSE))
         return NM_SETTING(s_tc);
 
     g_object_unref(s_tc);
@@ -4302,7 +4302,7 @@ make_wireless_setting(shvarFile *ifcfg, GError **error)
             bytes = g_bytes_new(value, value_len);
 
         ssid_len = g_bytes_get_size(bytes);
-        if (ssid_len > 32 || ssid_len == 0) {
+        if (ssid_len == 0 || ssid_len > NM_IW_ESSID_MAX_SIZE) {
             g_set_error(error,
                         NM_SETTINGS_ERROR,
                         NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -4678,7 +4678,9 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(
     {"--coalesce", NM_ETHTOOL_TYPE_COALESCE},
     {"--features", NM_ETHTOOL_TYPE_FEATURE},
     {"--offload", NM_ETHTOOL_TYPE_FEATURE},
+    {"--pause", NM_ETHTOOL_TYPE_PAUSE},
     {"--set-ring", NM_ETHTOOL_TYPE_RING},
+    {"-A", NM_ETHTOOL_TYPE_PAUSE},
     {"-C", NM_ETHTOOL_TYPE_COALESCE},
     {"-G", NM_ETHTOOL_TYPE_RING},
     {"-K", NM_ETHTOOL_TYPE_FEATURE}, );
@@ -4718,7 +4720,7 @@ parse_ethtool_option(const char *             value,
         w_iter = &words[2];
 
         while (w_iter && *w_iter) {
-            if (ethtool_type == NM_ETHTOOL_TYPE_FEATURE) {
+            if (NM_IN_SET(ethtool_type, NM_ETHTOOL_TYPE_FEATURE, NM_ETHTOOL_TYPE_PAUSE)) {
                 w_iter = _next_ethtool_options_nmternary(w_iter, ethtool_type, &ifcfg_option);
 
                 if (ifcfg_option.has_value) {
@@ -5021,6 +5023,7 @@ make_wired_setting(shvarFile *ifcfg, const char *file, NMSetting8021x **s_8021x,
     const char *                    cvalue;
     gs_free char *                  value = NULL;
     gboolean                        found = FALSE;
+    NMTernary                       accept_all_mac_addresses;
 
     s_wired = NM_SETTING_WIRED(nm_setting_wired_new());
 
@@ -5118,15 +5121,15 @@ make_wired_setting(shvarFile *ifcfg, const char *file, NMSetting8021x **s_8021x,
         for (i = 0; options && options[i]; i++) {
             const char *line = options[i];
             const char *equals;
-            gboolean    valid = FALSE;
 
             equals = strchr(line, '=');
-            if (equals) {
-                ((char *) equals)[0] = '\0';
-                valid                = nm_setting_wired_add_s390_option(s_wired, line, equals + 1);
-            }
-            if (!valid)
-                PARSE_WARNING("invalid s390 OPTION '%s'", line);
+            if (!equals)
+                continue;
+
+            /* Here we don't verify the key/value further. If the file contains invalid keys,
+             * we will later reject the connection as invalid. */
+            ((char *) equals)[0] = '\0';
+            nm_setting_wired_add_s390_option(s_wired, line, equals + 1);
         }
         found = TRUE;
     }
@@ -5179,6 +5182,15 @@ make_wired_setting(shvarFile *ifcfg, const char *file, NMSetting8021x **s_8021x,
     }
     nm_clear_g_free(&value);
 
+    accept_all_mac_addresses = svGetValueTernary(ifcfg, "ACCEPT_ALL_MAC_ADDRESSES");
+    if (accept_all_mac_addresses != NM_TERNARY_DEFAULT) {
+        g_object_set(s_wired,
+                     NM_SETTING_WIRED_ACCEPT_ALL_MAC_ADDRESSES,
+                     accept_all_mac_addresses,
+                     NULL);
+        found = TRUE;
+    }
+
     if (!found) {
         g_set_error(error,
                     NM_UTILS_ERROR,
@@ -6071,15 +6083,14 @@ make_vlan_setting(shvarFile *ifcfg, const char *file, GError **error)
                 v = iface_name + 4;
         }
 
-        if (v) {
-            int device_vlan_id;
-
-            /* Grab VLAN ID from interface name; this takes precedence over the
-             * separate VLAN_ID property for backwards compat.
+        if (vlan_id == -1 && v) {
+            /* Grab VLAN ID from interface name; The explicit VLAN_ID option takes precedence
+             * over detecting the ID based on PHYSDEV.
+             *
+             * Note that older versions of NetworkManager had a bug and this would overwrite the
+             * VLAN_ID in this case.
              */
-            device_vlan_id = _nm_utils_ascii_str_to_int64(v, 10, 0, 4095, -1);
-            if (device_vlan_id != -1)
-                vlan_id = device_vlan_id;
+            vlan_id = _nm_utils_ascii_str_to_int64(v, 10, 0, 4095, -1);
         }
     }
 
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
index 3f674dea..134bdf68 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
@@ -7,8 +7,9 @@
 
 #include "nms-ifcfg-rh-storage.h"
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-connection.h"
 #include "nms-ifcfg-rh-plugin.h"
 
@@ -88,7 +89,7 @@ static NMSIfcfgRHStorage *
 _storage_new(NMSIfcfgRHPlugin *plugin, const char *uuid, const char *filename)
 {
     nm_assert(NMS_IS_IFCFG_RH_PLUGIN(plugin));
-    nm_assert(!uuid || nm_utils_is_uuid(uuid));
+    nm_assert(!uuid || nm_uuid_is_normalized(uuid));
     nm_assert(filename && filename[0] == '/');
 
     return g_object_new(NMS_TYPE_IFCFG_RH_STORAGE,
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index 8da5de47..6f6035e1 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -9,7 +9,7 @@
 
 #include <stdlib.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 
 #include "nms-ifcfg-rh-common.h"
@@ -593,6 +593,9 @@ const char *const _nm_ethtool_ifcfg_names[] = {
     ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"),
     ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"),
     ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"),
+    ETHT_NAME(NM_ETHTOOL_ID_PAUSE_AUTONEG, "pause-autoneg"),
+    ETHT_NAME(NM_ETHTOOL_ID_PAUSE_RX, "pause-rx"),
+    ETHT_NAME(NM_ETHTOOL_ID_PAUSE_TX, "pause-tx"),
     ETHT_NAME(NM_ETHTOOL_ID_RING_RX, "rx"),
     ETHT_NAME(NM_ETHTOOL_ID_RING_RX_JUMBO, "rx-jumbo"),
     ETHT_NAME(NM_ETHTOOL_ID_RING_RX_MINI, "rx-mini"),
@@ -722,6 +725,15 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(
     {"rx-mini", NM_ETHTOOL_ID_RING_RX_MINI},
     {"tx", NM_ETHTOOL_ID_RING_TX}, );
 
+static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(
+    _get_ethtoolid_pause_by_name,
+    NMEthtoolID,
+    { nm_assert(name); },
+    { return NM_ETHTOOL_ID_UNKNOWN; },
+    {"pause-autoneg", NM_ETHTOOL_ID_PAUSE_AUTONEG},
+    {"pause-rx", NM_ETHTOOL_ID_PAUSE_RX},
+    {"pause-tx", NM_ETHTOOL_ID_PAUSE_TX}, );
+
 const NMEthtoolData *
 nms_ifcfg_rh_utils_get_ethtool_by_name(const char *name, NMEthtoolType ethtool_type)
 {
@@ -737,6 +749,9 @@ nms_ifcfg_rh_utils_get_ethtool_by_name(const char *name, NMEthtoolType ethtool_t
     case NM_ETHTOOL_TYPE_RING:
         id = _get_ethtoolid_ring_by_name(name);
         break;
+    case NM_ETHTOOL_TYPE_PAUSE:
+        id = _get_ethtoolid_pause_by_name(name);
+        break;
     default:
         nm_assert_not_reached();
         return NULL;
@@ -800,6 +815,7 @@ nms_ifcfg_rh_utils_is_numbered_tag_impl(const char *key,
     }
 
 const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
+    _KEY_TYPE("ACCEPT_ALL_MAC_ADDRESSES", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("ACD_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("ADDRESS", NMS_IFCFG_KEY_TYPE_IS_NUMBERED),
     _KEY_TYPE("AP_ISOLATION", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
@@ -1026,6 +1042,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
     _KEY_TYPE("STABLE_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("STP", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("SUBCHANNELS", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
+    _KEY_TYPE("TC_COMMIT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("TEAM_CONFIG", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("TEAM_MASTER", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("TEAM_MASTER_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
index 36ec9225..b61ce80a 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -7,7 +7,7 @@
 #define _UTILS_H_
 
 #include "nm-connection.h"
-#include "nm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-base.h"
 
 #include "shvar.h"
 
@@ -33,7 +33,7 @@ typedef struct {
     NMSIfcfgKeyTypeFlags key_flags;
 } NMSIfcfgKeyTypeInfo;
 
-extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[247];
+extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[249];
 
 const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx);
 
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index a968fce0..1c5bbbb4 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -15,8 +15,9 @@
 #include <unistd.h>
 #include <stdio.h>
 
-#include "nm-glib-aux/nm-enum-utils.h"
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-glib-aux/nm-enum-utils.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 #include "nm-manager.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wired.h"
@@ -32,9 +33,9 @@
 #include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
-#include "nm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-base.h"
 
 #include "nms-ifcfg-rh-common.h"
 #include "nms-ifcfg-rh-reader.h"
@@ -203,18 +204,18 @@ write_object(NMSetting8021x *                s_8021x,
     g_return_val_if_fail(ifcfg != NULL, FALSE);
     g_return_val_if_fail(objtype != NULL, FALSE);
 
-    scheme = (*(objtype->vtable->scheme_func))(s_8021x);
+    scheme = (*(objtype->vtable->scheme_func)) (s_8021x);
     switch (scheme) {
     case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
         break;
     case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-        blob = (*(objtype->vtable->blob_func))(s_8021x);
+        blob = (*(objtype->vtable->blob_func)) (s_8021x);
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PATH:
-        value = (*(objtype->vtable->path_func))(s_8021x);
+        value = (*(objtype->vtable->path_func)) (s_8021x);
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-        value = (*(objtype->vtable->uri_func))(s_8021x);
+        value = (*(objtype->vtable->uri_func)) (s_8021x);
         break;
     default:
         g_set_error(error,
@@ -227,8 +228,8 @@ write_object(NMSetting8021x *                s_8021x,
     /* Set the password for certificate/private key. */
     nm_sprintf_buf(secret_name, "%s_PASSWORD", objtype->ifcfg_rh_key);
     nm_sprintf_buf(secret_flags, "%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key);
-    password = (*(objtype->vtable->passwd_func))(s_8021x);
-    flags    = (*(objtype->vtable->pwflag_func))(s_8021x);
+    password = (*(objtype->vtable->passwd_func)) (s_8021x);
+    flags    = (*(objtype->vtable->pwflag_func)) (s_8021x);
     set_secret(ifcfg, secrets, secret_name, password, secret_flags, flags);
 
     if (!objtype->vtable->format_func)
@@ -299,6 +300,7 @@ write_blobs(GHashTable *blobs, GError **error)
                                         g_bytes_get_size(blob),
                                         0600,
                                         NULL,
+                                        NULL,
                                         &write_error)) {
             g_set_error(error,
                         NM_SETTINGS_ERROR,
@@ -862,7 +864,7 @@ write_wireless_setting(NMConnection *connection,
         return FALSE;
     }
     ssid_data = g_bytes_get_data(ssid, &ssid_len);
-    if (!ssid_len || ssid_len > 32) {
+    if (ssid_len == 0 || ssid_len > NM_IW_ESSID_MAX_SIZE) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_FAILED,
@@ -903,9 +905,9 @@ write_wireless_setting(NMConnection *connection,
         svSetValueStr(ifcfg, "ESSID", str->str);
         g_string_free(str, TRUE);
     } else {
-        char buf[33];
+        char buf[NM_IW_ESSID_MAX_SIZE + 1];
 
-        nm_assert(ssid_len <= 32);
+        nm_assert(ssid_len <= NM_IW_ESSID_MAX_SIZE);
         memcpy(buf, ssid_data, ssid_len);
         buf[ssid_len] = '\0';
         svSetValueStr(ifcfg, "ESSID", buf);
@@ -1075,25 +1077,18 @@ write_hostname_setting(NMConnection *connection, shvarFile *ifcfg)
     svSetValueInt64_cond(ifcfg, "HOSTNAME_ONLY_FROM_DEFAULT", t != NM_TERNARY_DEFAULT, t);
 }
 
-static gboolean
-write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
+static void
+write_wired_setting_impl(NMSettingWired *s_wired, shvarFile *ifcfg, gboolean is_virtual)
 {
-    NMSettingWired *   s_wired;
     const char *const *s390_subchannels;
-    guint32            mtu, num_opts, i;
+    guint32            mtu;
+    guint32            num_opts;
+    guint32            i;
     const char *const *macaddr_blacklist;
 
-    s_wired = nm_connection_get_setting_wired(connection);
-    if (!s_wired) {
-        g_set_error(error,
-                    NM_SETTINGS_ERROR,
-                    NM_SETTINGS_ERROR_FAILED,
-                    "Missing '%s' setting",
-                    NM_SETTING_WIRED_SETTING_NAME);
-        return FALSE;
-    }
-
-    svSetValueStr(ifcfg, "HWADDR", nm_setting_wired_get_mac_address(s_wired));
+    svSetValue(ifcfg,
+               "HWADDR",
+               nm_setting_wired_get_mac_address(s_wired) ?: (is_virtual ? "" : NULL));
 
     svSetValueStr(ifcfg, "MACADDR", nm_setting_wired_get_cloned_mac_address(s_wired));
 
@@ -1137,7 +1132,7 @@ write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
     svSetValueStr(ifcfg, "CTCPROT", nm_setting_wired_get_s390_option_by_key(s_wired, "ctcprot"));
 
     num_opts = nm_setting_wired_get_num_s390_options(s_wired);
-    if (s390_subchannels && num_opts) {
+    if (num_opts > 0) {
         nm_auto_free_gstring GString *tmp = NULL;
 
         for (i = 0; i < num_opts; i++) {
@@ -1167,8 +1162,42 @@ write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
             svSetValueStr(ifcfg, "OPTIONS", tmp->str);
     }
 
+    svSetValueTernary(ifcfg,
+                      "ACCEPT_ALL_MAC_ADDRESSES",
+                      nm_setting_wired_get_accept_all_mac_addresses(s_wired));
+}
+
+static gboolean
+write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+    NMSettingWired *s_wired;
+
+    s_wired = nm_connection_get_setting_wired(connection);
+    if (!s_wired) {
+        g_set_error(error,
+                    NM_SETTINGS_ERROR,
+                    NM_SETTINGS_ERROR_FAILED,
+                    "Missing '%s' setting",
+                    NM_SETTING_WIRED_SETTING_NAME);
+        return FALSE;
+    }
+
     svSetValueStr(ifcfg, "TYPE", TYPE_ETHERNET);
 
+    write_wired_setting_impl(s_wired, ifcfg, FALSE);
+    return TRUE;
+}
+
+static gboolean
+write_wired_for_virtual(NMConnection *connection, shvarFile *ifcfg)
+{
+    NMSettingWired *s_wired;
+
+    s_wired = nm_connection_get_setting_wired(connection);
+    if (!s_wired)
+        return FALSE;
+
+    write_wired_setting_impl(s_wired, ifcfg, TRUE);
     return TRUE;
 }
 
@@ -1334,6 +1363,26 @@ write_ethtool_setting(NMConnection *connection, shvarFile *ifcfg, GError **error
             g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id));
             g_string_append_printf(str, " %" G_GUINT32_FORMAT, u32);
         }
+        for (ethtool_id = _NM_ETHTOOL_ID_PAUSE_FIRST; ethtool_id <= _NM_ETHTOOL_ID_PAUSE_LAST;
+             ethtool_id++) {
+            nm_assert(nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id));
+            if (!nm_setting_option_get_boolean(NM_SETTING(s_ethtool),
+                                               nm_ethtool_data[ethtool_id]->optname,
+                                               &b))
+                continue;
+
+            _ethtool_gstring_prepare(&str, &is_first, 'A', iface);
+            g_string_append_c(str, ' ');
+            g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id));
+            g_string_append(str, b ? " on" : " off");
+        }
+
+        if (!str) {
+            /* Write an empty dummy "-A" option without arguments. This is to
+             * ensure that the reader will create an (all default) NMSettingEthtool.
+             * Also, it seems that `ethtool -A "$IFACE"` is silently accepted. */
+            _ethtool_gstring_prepare(&str, &is_first, 'A', iface);
+        }
     }
 
     if (str) {
@@ -1367,35 +1416,6 @@ vlan_priority_maplist_to_stringlist(NMSettingVlan *s_vlan, NMVlanPriorityMap map
 }
 
 static gboolean
-write_wired_for_virtual(NMConnection *connection, shvarFile *ifcfg)
-{
-    NMSettingWired *s_wired;
-    gboolean        has_wired = FALSE;
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    if (s_wired) {
-        const char *device_mac, *cloned_mac;
-        guint32     mtu;
-
-        has_wired = TRUE;
-
-        device_mac = nm_setting_wired_get_mac_address(s_wired);
-        svSetValue(ifcfg, "HWADDR", device_mac ?: "");
-
-        cloned_mac = nm_setting_wired_get_cloned_mac_address(s_wired);
-        svSetValueStr(ifcfg, "MACADDR", cloned_mac);
-
-        svSetValueStr(ifcfg,
-                      "GENERATE_MAC_ADDRESS_MASK",
-                      nm_setting_wired_get_generate_mac_address_mask(s_wired));
-
-        mtu = nm_setting_wired_get_mtu(s_wired);
-        svSetValueInt64_cond(ifcfg, "MTU", mtu != 0, mtu);
-    }
-    return has_wired;
-}
-
-static gboolean
 write_vlan_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error)
 {
     NMSettingVlan *s_vlan;
@@ -1542,36 +1562,40 @@ write_team_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 
 static gboolean
 write_bridge_vlans(NMSetting * setting,
-                   const char *property_name,
+                   gboolean    is_port,
                    shvarFile * ifcfg,
                    const char *key,
                    GError **   error)
 {
-    gs_unref_ptrarray GPtrArray *vlans = NULL;
-    NMBridgeVlan *               vlan;
-    GString *                    string;
-    guint                        i;
+    GPtrArray *              vlans;
+    NMBridgeVlan *           vlan;
+    nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(0, FALSE);
+    guint                    i;
 
-    g_object_get(setting, property_name, &vlans, NULL);
+    if (is_port)
+        vlans = _nm_setting_bridge_port_get_vlans(NM_SETTING_BRIDGE_PORT(setting));
+    else
+        vlans = _nm_setting_bridge_get_vlans(NM_SETTING_BRIDGE(setting));
 
     if (!vlans || !vlans->len)
         return TRUE;
 
-    string = g_string_new("");
     for (i = 0; i < vlans->len; i++) {
         gs_free char *vlan_str = NULL;
 
         vlan     = vlans->pdata[i];
         vlan_str = nm_bridge_vlan_to_str(vlan, error);
-        if (!vlan_str)
-            return FALSE;
-        if (string->len > 0)
-            g_string_append(string, ",");
-        nm_utils_escaped_tokens_escape_gstr_assert(vlan_str, ",", string);
+        if (!vlan_str) {
+            /* nm_bridge_vlan_to_str() cannot fail (for now). */
+            nm_assert_not_reached();
+            continue;
+        }
+        if (strbuf.len > 0)
+            nm_str_buf_append_c(&strbuf, ',');
+        nm_str_buf_append(&strbuf, nm_utils_escaped_tokens_escape_unnecessary(vlan_str, ","));
     }
 
-    svSetValueStr(ifcfg, key, string->str);
-    g_string_free(string, TRUE);
+    svSetValueStr(ifcfg, key, nm_str_buf_get_str(&strbuf));
     return TRUE;
 }
 
@@ -1807,11 +1831,7 @@ write_bridge_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired
         svSetValueStr(ifcfg, "BRIDGING_OPTS", opts->str);
     g_string_free(opts, TRUE);
 
-    if (!write_bridge_vlans((NMSetting *) s_bridge,
-                            NM_SETTING_BRIDGE_VLANS,
-                            ifcfg,
-                            "BRIDGE_VLANS",
-                            error))
+    if (!write_bridge_vlans((NMSetting *) s_bridge, FALSE, ifcfg, "BRIDGE_VLANS", error))
         return FALSE;
 
     svSetValueStr(ifcfg, "TYPE", TYPE_BRIDGE);
@@ -1862,11 +1882,7 @@ write_bridge_port_setting(NMConnection *connection, shvarFile *ifcfg, GError **e
         svSetValueStr(ifcfg, "BRIDGING_OPTS", string->str);
     g_string_free(string, TRUE);
 
-    if (!write_bridge_vlans((NMSetting *) s_port,
-                            NM_SETTING_BRIDGE_PORT_VLANS,
-                            ifcfg,
-                            "BRIDGE_PORT_VLANS",
-                            error))
+    if (!write_bridge_vlans((NMSetting *) s_port, TRUE, ifcfg, "BRIDGE_PORT_VLANS", error))
         return FALSE;
 
     return TRUE;
@@ -2511,46 +2527,46 @@ write_sriov_setting(NMConnection *connection, shvarFile *ifcfg)
     }
 }
 
-static gboolean
-write_tc_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
+static void
+write_tc_setting(NMConnection *connection, shvarFile *ifcfg)
 {
     NMSettingTCConfig *s_tc;
-    guint              i, num, n;
+    guint              num_qdiscs;
+    guint              num_filters;
+    guint              i;
+    guint              n;
     char               tag[64];
 
     s_tc = nm_connection_get_setting_tc_config(connection);
     if (!s_tc)
-        return TRUE;
+        return;
 
-    num = nm_setting_tc_config_get_num_qdiscs(s_tc);
-    for (n = 1, i = 0; i < num; i++) {
+    num_qdiscs = nm_setting_tc_config_get_num_qdiscs(s_tc);
+    for (n = 1, i = 0; i < num_qdiscs; i++) {
         NMTCQdisc *   qdisc;
         gs_free char *str = NULL;
 
         qdisc = nm_setting_tc_config_get_qdisc(s_tc, i);
-        str   = nm_utils_tc_qdisc_to_str(qdisc, error);
-        if (!str)
-            return FALSE;
-
+        str   = nm_utils_tc_qdisc_to_str(qdisc, NULL);
+        nm_assert(str);
         svSetValueStr(ifcfg, numbered_tag(tag, "QDISC", n), str);
         n++;
     }
 
-    num = nm_setting_tc_config_get_num_tfilters(s_tc);
-    for (n = 1, i = 0; i < num; i++) {
+    num_filters = nm_setting_tc_config_get_num_tfilters(s_tc);
+    for (n = 1, i = 0; i < num_filters; i++) {
         NMTCTfilter * tfilter;
         gs_free char *str = NULL;
 
         tfilter = nm_setting_tc_config_get_tfilter(s_tc, i);
-        str     = nm_utils_tc_tfilter_to_str(tfilter, error);
-        if (!str)
-            return FALSE;
-
+        str     = nm_utils_tc_tfilter_to_str(tfilter, NULL);
+        nm_assert(str);
         svSetValueStr(ifcfg, numbered_tag(tag, "FILTER", n), str);
         n++;
     }
 
-    return TRUE;
+    if (num_qdiscs == 0 && num_filters == 0)
+        svSetValueBoolean(ifcfg, "TC_COMMIT", TRUE);
 }
 
 static void
@@ -3373,9 +3389,7 @@ do_write_construct(NMConnection *                  connection,
     write_match_setting(connection, ifcfg);
     write_hostname_setting(connection, ifcfg);
     write_sriov_setting(connection, ifcfg);
-
-    if (!write_tc_setting(connection, ifcfg, error))
-        return FALSE;
+    write_tc_setting(connection, ifcfg);
 
     route_path_is_svformat = utils_has_route_file_new_syntax(route_path);
 
diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.c b/src/core/settings/plugins/ifcfg-rh/shvar.c
index 386bcac7..80644b64 100644
--- a/src/core/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/core/settings/plugins/ifcfg-rh/shvar.c
@@ -14,10 +14,11 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "nm-core-internal.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-core-utils.h"
-#include "nm-glib-aux/nm-enum-utils.h"
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-glib-aux/nm-enum-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 #include "c-list/src/c-list.h"
 #include "nms-ifcfg-rh-utils.h"
 
@@ -152,17 +153,42 @@ _escape_ansic(const char *source)
     const char *p;
     char *      dest;
     char *      q;
+    gsize       n_alloc;
 
     nm_assert(source);
 
-    p = (const char *) source;
-    /* Each source byte needs maximally four destination chars (\777) */
-    q = dest = g_malloc(strlen(source) * 4 + 1 + 3);
+    n_alloc = 4;
+    for (p = source; p[0]; p++) {
+        switch (*p) {
+        case '\b':
+        case '\f':
+        case '\n':
+        case '\r':
+        case '\t':
+        case '\v':
+        case '\\':
+        case '"':
+        case '\'':
+            n_alloc += 2;
+            break;
+        default:
+            if ((*p < ' ') || (*p >= 0177))
+                n_alloc += 4;
+            else
+                n_alloc += 1;
+            break;
+        }
+    }
+
+    dest = g_malloc(n_alloc);
+
+    q = dest;
 
     *q++ = '$';
     *q++ = '\'';
 
-    while (*p) {
+    for (p = source; p[0]; p++) {
+        nm_assert(q < &dest[n_alloc]);
         switch (*p) {
         case '\b':
             *q++ = '\\';
@@ -204,12 +230,11 @@ _escape_ansic(const char *source)
                 *q++ = *p;
             break;
         }
-        p++;
     }
     *q++ = '\'';
     *q++ = '\0';
 
-    nm_assert(q - dest <= strlen(source) * 4 + 1 + 3);
+    nm_assert(q - dest == n_alloc);
 
     return dest;
 }
@@ -217,7 +242,7 @@ _escape_ansic(const char *source)
 /*****************************************************************************/
 
 #define _char_req_escape(ch)     NM_IN_SET(ch, '"', '\\', '$', '`')
-#define _char_req_escape_old(ch) NM_IN_SET(ch, '"', '\\', '\'', '$', '`', '~')
+#define _char_req_escape_old(ch) NM_IN_SET(ch, '"', '\\', '$', '`', '\'', '~')
 #define _char_req_quotes(ch)     NM_IN_SET(ch, ' ', '\'', '~', '\t', '|', '&', ';', '(', ')', '<', '>')
 
 const char *
@@ -226,8 +251,10 @@ svEscape(const char *s, char **to_free)
     char *new;
     gsize    mangle          = 0;
     gboolean requires_quotes = FALSE;
-    int      newlen;
-    size_t   i, j, slen;
+    gsize    n_alloc;
+    gsize    slen;
+    gsize    i;
+    gsize    j;
 
     for (slen = 0; s[slen]; slen++) {
         if (_char_req_escape(s[slen]))
@@ -247,8 +274,8 @@ svEscape(const char *s, char **to_free)
         return s;
     }
 
-    newlen = slen + mangle + 3; /* 3 is extra ""\0 */
-    new    = g_malloc(newlen);
+    n_alloc = slen + mangle + 3; /* 3 is extra ""\0 */
+    new     = g_malloc(n_alloc);
 
     j        = 0;
     new[j++] = '"';
@@ -260,7 +287,7 @@ svEscape(const char *s, char **to_free)
     new[j++] = '"';
     new[j++] = '\0';
 
-    nm_assert(j == slen + mangle + 3);
+    nm_assert(j == n_alloc);
 
     *to_free = new;
     return new;
@@ -318,14 +345,14 @@ _ch_hex_get(char ch)
 }
 
 static void
-_gstr_init(GString **str, const char *value, gsize i)
+_strbuf_init(NMStrBuf *str, const char *value, gsize i)
 {
     nm_assert(str);
     nm_assert(value);
 
-    if (!(*str)) {
-        /* if @str is not yet initialized, it allocates
-         * a new GString and copies @i characters from
+    if (str->allocated == 0) {
+        /* if @str is not yet initialized, it initializes
+         * a new NMStrBuf and copies @i characters from
          * @value over.
          *
          * Unescaping usually does not extend the length of a string,
@@ -335,20 +362,20 @@ _gstr_init(GString **str, const char *value, gsize i)
          * (FACTOR*strlen(value) + CONST), which is non trivial to get
          * right in all cases. Also, we would have to provision for the
          * very unlikely extreme case.
-         * Instead, use a GString buffer which can grow as needed. But for an
+         * Instead, use a NMStrBuf buffer which can grow as needed. But for an
          * initial guess, strlen(value) is a good start */
-        *str = g_string_new_len(NULL, strlen(value) + 3);
-        if (i)
-            g_string_append_len(*str, value, i);
+        nm_str_buf_maybe_expand(str, strlen(value) + 3u, FALSE);
+        nm_str_buf_append_len(str, value, i);
     }
 }
 
 const char *
 svUnescape(const char *value, char **to_free)
 {
-    gsize    i, j;
-    GString *str                      = NULL;
+    NMStrBuf str                      = NM_STR_BUF_INIT(0, FALSE);
     int      looks_like_old_svescaped = -1;
+    gsize    i;
+    gsize    j;
 
     /* we handle bash syntax here (note that ifup has #!/bin/bash.
      * Thus, see https://www.gnu.org/software/bash/manual/html_node/Quoting.html#Quoting */
@@ -395,20 +422,20 @@ svUnescape(const char *value, char **to_free)
 
         if (value[i] == '\\') {
             /* backslash escape */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i++;
             if (G_UNLIKELY(value[i] == '\0')) {
                 /* we don't support line continuation */
                 goto out_error;
             }
-            g_string_append_c(str, value[i]);
+            nm_str_buf_append_c(&str, value[i]);
             i++;
             goto loop1_next;
         }
 
         if (value[i] == '\'') {
             /* single quotes */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i++;
             j = i;
             while (TRUE) {
@@ -420,14 +447,14 @@ svUnescape(const char *value, char **to_free)
                     break;
                 j++;
             }
-            g_string_append_len(str, &value[i], j - i);
+            nm_str_buf_append_len(&str, &value[i], j - i);
             i = j + 1;
             goto loop1_next;
         }
 
         if (value[i] == '"') {
             /* double quotes */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i++;
             while (TRUE) {
                 if (value[i] == '"') {
@@ -466,11 +493,11 @@ svUnescape(const char *value, char **to_free)
                         if (looks_like_old_svescaped < 0)
                             looks_like_old_svescaped = _looks_like_old_svescaped(value);
                         if (!looks_like_old_svescaped)
-                            g_string_append_c(str, '\\');
+                            nm_str_buf_append_c(&str, '\\');
                     } else
-                        g_string_append_c(str, '\\');
+                        nm_str_buf_append_c(&str, '\\');
                 }
-                g_string_append_c(str, value[i]);
+                nm_str_buf_append_c(&str, value[i]);
                 i++;
             }
             goto loop1_next;
@@ -478,7 +505,7 @@ svUnescape(const char *value, char **to_free)
 
         if (value[i] == '$' && value[i + 1] == '\'') {
             /* ANSI-C Quoting */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i += 2;
             while (TRUE) {
                 char ch;
@@ -552,7 +579,7 @@ svUnescape(const char *value, char **to_free)
                                 }
                             }
                             /* like bash, we cut too large numbers off. E.g. A=$'\772' becomes 0xfa  */
-                            g_string_append_c(str, (guint8) v);
+                            nm_str_buf_append_c(&str, (guint8) v);
                         } else if (NM_IN_SET(value[i], 'x', 'u', 'U')) {
                             const char escape_type = value[i];
                             int max_digits = escape_type == 'x' ? 2 : escape_type == 'u' ? 4 : 8;
@@ -561,8 +588,7 @@ svUnescape(const char *value, char **to_free)
                             i++;
                             if (!_ch_hex_is(value[i])) {
                                 /* missing hex value after "\x" escape. This is treated like no escaping. */
-                                g_string_append_c(str, '\\');
-                                g_string_append_c(str, escape_type);
+                                nm_str_buf_append_c(&str, '\\', escape_type);
                             } else {
                                 v = _ch_hex_get(value[i]);
                                 i++;
@@ -574,22 +600,21 @@ svUnescape(const char *value, char **to_free)
                                     i++;
                                 }
                                 if (escape_type == 'x')
-                                    g_string_append_c(str, v);
+                                    nm_str_buf_append_c(&str, v);
                                 else {
                                     /* we treat the unicode escapes as utf-8 encoded values. */
-                                    g_string_append_unichar(str, v);
+                                    nm_str_buf_append_unichar(&str, v);
                                 }
                             }
                         } else {
-                            g_string_append_c(str, '\\');
-                            g_string_append_c(str, value[i]);
+                            nm_str_buf_append_c(&str, '\\', value[i]);
                             i++;
                         }
                         goto loop_ansic_next;
                     }
                 } else
                     ch = value[i];
-                g_string_append_c(str, ch);
+                nm_str_buf_append_c(&str, ch);
                 i++;
 loop_ansic_next:;
             }
@@ -603,8 +628,8 @@ loop_ansic_next:;
         }
 
         /* an unquoted, regular character. Just consume it directly. */
-        if (str)
-            g_string_append_c(str, value[i]);
+        if (str.allocated > 0)
+            nm_str_buf_append_c(&str, value[i]);
         i++;
 
 loop1_next:;
@@ -614,18 +639,19 @@ loop1_next:;
 
 out_value:
     if (i == 0) {
-        nm_assert(!str);
+        nm_assert(str.allocated == 0);
+        nm_assert(!str._priv_str);
         *to_free = NULL;
         return "";
     }
 
-    if (str) {
-        if (str->len == 0 || str->str[0] == '\0') {
-            g_string_free(str, TRUE);
+    if (str.allocated > 0) {
+        if (str.len == 0 || nm_str_buf_get_str_unsafe(&str)[0] == '\0') {
+            nm_str_buf_destroy(&str);
             *to_free = NULL;
             return "";
         } else {
-            *to_free = g_string_free(str, FALSE);
+            *to_free = nm_str_buf_finalize(&str, NULL);
             return *to_free;
         }
     }
@@ -639,8 +665,7 @@ out_value:
     return value;
 
 out_error:
-    if (str)
-        g_string_free(str, TRUE);
+    nm_str_buf_destroy(&str);
     *to_free = NULL;
     return NULL;
 }
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
index d81d9187..aebcd2b8 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:22:15:59:62:97
 TYPE=Ethernet
+HWADDR=00:22:15:59:62:97
 BRIDGING_OPTS="priority=28 hairpin_mode=1"
 NAME="System test-bridge-component"
 UUID=${UUID}
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
index 2e69c5e7..71d2cda8 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:22:15:59:62:97
 TYPE=Ethernet
+HWADDR=00:22:15:59:62:97
 NAME="System test-bridge-component"
 UUID=${UUID}
 DEVICE=eth0
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
index 59a6f79b..240cd5c1 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:11:22:33:44:EE
 TYPE=Ethernet
+HWADDR=00:11:22:33:44:EE
 KEY_MGMT=IEEE8021X
 IEEE_8021X_EAP_METHODS=PEAP
 IEEE_8021X_IDENTITY="Jara Cimrman"
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
index a8ff8df3..d31d8ff5 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
@@ -1,6 +1,6 @@
+TYPE=Ethernet
 HWADDR=31:33:33:37:BE:CD
 MTU=1492
-TYPE=Ethernet
 BRIDGING_OPTS="priority=50 path_cost=33"
 BRIDGE_PORT_VLANS="1 untagged,2 pvid,4-4094 untagged"
 NAME="Test Write Bridge Component"
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
index cd8fc96f..cc29a2cc 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
@@ -1,6 +1,6 @@
+TYPE=Ethernet
 HWADDR=31:33:33:37:BE:CD
 MTU=1492
-TYPE=Ethernet
 PROXY_METHOD=none
 BROWSER_ONLY=no
 BOOTPROTO=none
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
index a28c5c1c..8197f320 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:16:41:11:22:33
 TYPE=Ethernet
+HWADDR=00:16:41:11:22:33
 PROXY_METHOD=none
 BROWSER_ONLY=no
 BOOTPROTO=dhcp
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected
new file mode 100644
index 00000000..4df768b4
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+TC_COMMIT=yes
+BOOTPROTO=none
+IPADDR=1.1.1.3
+PREFIX=24
+GATEWAY=1.1.1.1
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write TC config"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use
new file mode 100644
index 00000000..fc9c8a45
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use
@@ -0,0 +1,4 @@
+VLAN=yes
+TYPE=Vlan
+DEVICE=eth0.9
+VLAN_ID=10
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected
new file mode 100644
index 00000000..a7be14ce
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected
@@ -0,0 +1,15 @@
+VLAN=yes
+TYPE=Vlan
+PHYSDEV=eth0
+VLAN_ID=10
+REORDER_HDR=yes
+GVRP=no
+MVRP=no
+HWADDR=
+PROXY_METHOD=none
+BROWSER_ONLY=no
+IPV6INIT=no
+NAME="Vlan test-vlan-vlanid-use"
+UUID=${UUID}
+DEVICE=eth0.9
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected
new file mode 100644
index 00000000..5186f90c
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected
@@ -0,0 +1,14 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected
new file mode 100644
index 00000000..5632ebec
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="-A net0"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected
new file mode 100644
index 00000000..96eec43b
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="autoneg on"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected
new file mode 100644
index 00000000..90250a30
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="-K net0 rx on"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected
new file mode 100644
index 00000000..721eacb2
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="autoneg on ; -K net0 rx on"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 59127d01..6193b952 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -17,7 +17,8 @@
 #include <linux/if_ether.h>
 #include <linux/if_infiniband.h>
 
-#include "nm-glib-aux/nm-json-aux.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-json-aux.h"
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wired.h"
@@ -36,9 +37,9 @@
 #include "nm-setting-serial.h"
 #include "nm-setting-vlan.h"
 #include "nm-setting-dcb.h"
-#include "nm-core-internal.h"
-#include "nm-base/nm-ethtool-base.h"
-#include "nm-base/nm-ethtool-utils-base.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-utils-base.h"
 
 #include "NetworkManagerUtils.h"
 
@@ -180,8 +181,20 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c
     }
 
     if (len_expectd != len_written || memcmp(content_expectd, content_written, len_expectd) != 0) {
-        if (g_getenv("NMTST_IFCFG_RH_UPDATE_EXPECTED")
-            || nm_streq0(g_getenv("NM_TEST_REGENERATE"), "1")) {
+        static int rewrite_static = 0;
+        int        rewrite;
+
+        rewrite = g_atomic_int_get(&rewrite_static);
+        if (G_UNLIKELY(rewrite == 0)) {
+            rewrite = (g_getenv("NMTST_IFCFG_RH_UPDATE_EXPECTED")
+                       || nm_streq0(g_getenv("NM_TEST_REGENERATE"), "1"))
+                          ? -1
+                          : 1;
+            if (!g_atomic_int_compare_and_exchange(&rewrite_static, 0, rewrite))
+                g_assert_not_reached();
+        }
+
+        if (rewrite > 0) {
             if (uuid) {
                 gs_free char *search = g_strdup_printf("UUID=%s\n", uuid);
                 const char *  s;
@@ -209,15 +222,16 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c
             success = g_file_set_contents(expected, content_written, len_written, &error);
             nmtst_assert_success(success, error);
         } else {
-            g_error("The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set "
-                    "NMTST_IFCFG_RH_UPDATE_EXPECTED=yes to update the files "
-                    "inplace\n\n>>>%s<<<\n\n>>>%s<<<\n",
-                    filename,
-                    len_written,
-                    expected,
-                    len_expectd,
-                    content_written,
-                    content_expectd);
+            g_error(
+                "The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set "
+                "NMTST_IFCFG_RH_UPDATE_EXPECTED=yes (or NM_TEST_REGENERATE=1) to update the files "
+                "inplace\n\n>>>%s<<<\n\n>>>%s<<<\n",
+                filename,
+                len_written,
+                expected,
+                len_expectd,
+                content_written,
+                content_expectd);
         }
     }
 }
@@ -359,8 +373,10 @@ _writer_new_connection_reread(NMConnection * connection,
 
     if (out_filename)
         *out_filename = filename;
-    else
+    else {
+        nmtst_file_unlink(filename);
         g_free(filename);
+    }
 }
 
 static void
@@ -2519,7 +2535,7 @@ test_write_dns_options(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test DNS options",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -3933,6 +3949,76 @@ test_read_wired_unknown_ethtool_opt(void)
 }
 
 static void
+test_roundtrip_ethtool(void)
+{
+    gs_unref_object NMConnection *connection = NULL;
+    NMSetting *                   s_ethtool;
+    NMSetting *                   s_wired;
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-1.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_ethtool  = nm_setting_ethtool_new();
+    nm_connection_add_setting(connection, s_ethtool);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-2.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_wired    = nm_connection_get_setting(connection, NM_TYPE_SETTING_WIRED);
+    g_object_set(s_wired, NM_SETTING_WIRED_AUTO_NEGOTIATE, TRUE, NULL);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-3.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_ethtool  = nm_setting_ethtool_new();
+    nm_connection_add_setting(connection, s_ethtool);
+    nm_setting_option_set_boolean(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX, TRUE);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-4.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_wired    = nm_connection_get_setting(connection, NM_TYPE_SETTING_WIRED);
+    g_object_set(s_wired, NM_SETTING_WIRED_AUTO_NEGOTIATE, TRUE, NULL);
+    s_ethtool = nm_setting_ethtool_new();
+    nm_connection_add_setting(connection, s_ethtool);
+    nm_setting_option_set_boolean(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX, TRUE);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-5.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+}
+
+static void
 test_read_wifi_hidden(void)
 {
     NMConnection *       connection;
@@ -3977,7 +4063,7 @@ test_write_wifi_hidden(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wi-Fi Hidden",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4062,7 +4148,7 @@ test_write_wifi_mac_random(gconstpointer user_data)
                  NM_SETTING_CONNECTION_ID,
                  val,
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4122,7 +4208,7 @@ test_write_wired_wake_on_lan(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Wake-on-LAN",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -4305,7 +4391,7 @@ test_write_wifi_band_a(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wi-Fi Band A",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4363,7 +4449,7 @@ test_write_wifi_ap_mode(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wi-Fi AP Mode",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4676,7 +4762,7 @@ test_write_wired_static(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
@@ -4831,7 +4917,7 @@ test_write_wired_static_with_generic(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
@@ -4999,7 +5085,7 @@ test_write_wired_dhcp(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired DHCP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5111,7 +5197,7 @@ test_write_routing_rules(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Routing Rules",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5169,7 +5255,7 @@ test_write_wired_match(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired with Match setting",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5315,7 +5401,7 @@ test_write_wired_static_ip6_only(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static IP6 Only",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5432,7 +5518,7 @@ test_write_wired_static_ip6_only_gw(gconstpointer user_data)
                  NM_SETTING_CONNECTION_ID,
                  id,
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5587,7 +5673,7 @@ test_write_wired_static_routes(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static Routes",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5711,7 +5797,7 @@ test_write_wired_dhcp_8021x_peap_mschapv2(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired DHCP 802.1x PEAP MSCHAPv2",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5813,7 +5899,7 @@ test_write_wired_8021x_tls(gconstpointer test_data)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired 802.1x TLS Blobs",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5983,7 +6069,7 @@ test_write_wired_aliases(void)
                  NM_SETTING_CONNECTION_ID,
                  "alias0",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -6110,7 +6196,7 @@ test_write_gateway(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Static Addresses Gateway",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -6187,7 +6273,7 @@ test_write_wifi_open(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi Open",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6273,7 +6359,7 @@ test_write_wifi_open_hex_ssid(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi Open Hex SSID",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6347,7 +6433,7 @@ test_write_wifi_wep(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6445,7 +6531,7 @@ test_write_wifi_wep_adhoc(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP AdHoc",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6539,7 +6625,7 @@ test_write_wifi_wep_passphrase(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP Passphrase",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6634,7 +6720,7 @@ test_write_wifi_wep_40_ascii(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP 40 ASCII",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6732,7 +6818,7 @@ test_write_wifi_wep_104_ascii(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP 104 ASCII",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6833,7 +6919,7 @@ test_write_wifi_leap(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi LEAP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6931,7 +7017,7 @@ test_write_wifi_leap_secret_flags(gconstpointer data)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi LEAP Secret Flags",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -7037,7 +7123,7 @@ test_write_wifi_wpa_psk(gconstpointer test_data)
                  NM_SETTING_CONNECTION_ID,
                  args.name,
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7142,7 +7228,7 @@ test_write_wifi_wpa_psk_adhoc(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA PSK",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7252,7 +7338,7 @@ test_write_wifi_wpa_eap_tls(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA EAP-TLS",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7295,8 +7381,8 @@ test_write_wifi_wpa_eap_tls(void)
     g_object_set(s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
     g_object_set(s_8021x,
                  NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
-                 (guint)(NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE
-                         | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE),
+                 (guint) (NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE
+                          | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE),
                  NULL);
 
     nm_setting_802_1x_add_eap_method(s_8021x, "tls");
@@ -7379,7 +7465,7 @@ test_write_wifi_wpa_eap_ttls_tls(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA EAP-TTLS (TLS)",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7515,7 +7601,7 @@ test_write_wifi_wpa_eap_ttls_mschapv2(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA EAP-TTLS (MSCHAPv2)",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7630,7 +7716,7 @@ test_write_wifi_wpa_then_open(void)
                  NM_SETTING_CONNECTION_ID,
                  "random wifi connection",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7747,7 +7833,7 @@ test_write_wifi_wpa_then_wep_with_perms(void)
                  NM_SETTING_CONNECTION_ID,
                  "random wifi connection 2",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_PERMISSIONS,
@@ -7872,7 +7958,7 @@ test_write_wifi_dynamic_wep_leap(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi Dynamic WEP LEAP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -7969,7 +8055,7 @@ test_write_wired_qeth_dhcp(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired qeth Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8043,7 +8129,7 @@ test_write_wired_ctc_dhcp(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired ctc Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -8118,7 +8204,7 @@ test_write_permissions(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Permissions",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8187,7 +8273,7 @@ test_write_wifi_wep_agent_keys(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP Agent Owned",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -8273,7 +8359,7 @@ test_write_wired_pppoe(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired PPPoE",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8334,7 +8420,7 @@ test_write_vpn(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write VPN",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8392,7 +8478,7 @@ test_write_mobile_broadband(gconstpointer data)
                  NM_SETTING_CONNECTION_ID,
                  gsm ? "Test Write GSM" : "Test Write CDMA",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8510,7 +8596,7 @@ test_write_bridge_main(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bridge Main",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -8639,7 +8725,7 @@ test_write_bridge_component(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bridge Component",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8787,6 +8873,38 @@ test_read_vlan_only_vlan_id(void)
 }
 
 static void
+test_read_vlan_vlanid_use(void)
+{
+    nmtst_auto_unlinkfile char *testfile     = NULL;
+    gs_unref_object NMConnection *connection = NULL;
+    gs_unref_object NMConnection *reread     = NULL;
+    NMSettingVlan *               s_vlan;
+
+    connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-vlanid-use",
+                                       NULL,
+                                       TYPE_ETHERNET,
+                                       NULL);
+
+    g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "eth0.9");
+
+    s_vlan = nm_connection_get_setting_vlan(connection);
+    g_assert(s_vlan);
+
+    g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0");
+    g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 10);
+    g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS);
+
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test-vlan-vlanid-use.cexpected",
+                           &testfile);
+
+    reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL);
+
+    nmtst_assert_connection_equals(connection, TRUE, reread, FALSE);
+}
+
+static void
 test_read_vlan_only_device(void)
 {
     NMConnection * connection;
@@ -8991,7 +9109,7 @@ test_write_vlan_reorder_hdr(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write VLAN reorder_hdr",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  FALSE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9046,7 +9164,7 @@ test_write_ethernet_missing_ipv6(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Ethernet Without IPv6 Setting",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9170,7 +9288,7 @@ test_write_bond_main(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bond Main",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -9270,7 +9388,7 @@ test_write_bond_slave(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bond Slave",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9357,7 +9475,7 @@ test_write_infiniband(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write InfiniBand",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9446,7 +9564,7 @@ test_write_bond_slave_ib(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bond Slave InfiniBand",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9604,7 +9722,7 @@ test_write_dcb_basic(void)
                  NM_SETTING_CONNECTION_ID,
                  "dcb-test",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -9850,7 +9968,7 @@ test_write_fcoe_mode(gconstpointer user_data)
                  NM_SETTING_CONNECTION_ID,
                  "fcoe-test",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -9968,7 +10086,7 @@ test_write_team_master(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Team Master",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
                  "team0",
                  NM_SETTING_CONNECTION_TYPE,
@@ -10060,7 +10178,7 @@ test_write_team_port(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Team Port",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NM_SETTING_CONNECTION_MASTER,
@@ -10119,7 +10237,7 @@ test_write_team_infiniband_port(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Team Infiniband Port",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_INFINIBAND_SETTING_NAME,
                  NM_SETTING_CONNECTION_MASTER,
@@ -10309,7 +10427,7 @@ test_write_proxy_basic(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Proxy Basic",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -10999,7 +11117,7 @@ test_sriov_write(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write SR-IOV config",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -11109,6 +11227,85 @@ test_tc_read(void)
 }
 
 static void
+test_tc_write_empty(void)
+{
+    nmtst_auto_unlinkfile char *testfile     = NULL;
+    gs_unref_object NMConnection *connection = NULL;
+    gs_unref_object NMConnection *reread     = NULL;
+    NMSettingConnection *         s_con;
+    NMSettingIPConfig *           s_ip4;
+    NMSettingIPConfig *           s_ip6;
+    NMSettingWired *              s_wired;
+    NMSettingTCConfig *           s_tc;
+    NMIPAddress *                 addr;
+    GError *                      error = NULL;
+
+    connection = nm_simple_connection_new();
+
+    /* Connection setting */
+    s_con = (NMSettingConnection *) nm_setting_connection_new();
+    nm_connection_add_setting(connection, NM_SETTING(s_con));
+
+    g_object_set(s_con,
+                 NM_SETTING_CONNECTION_ID,
+                 "Test Write TC config",
+                 NM_SETTING_CONNECTION_UUID,
+                 nm_uuid_generate_random_str_a(),
+                 NM_SETTING_CONNECTION_AUTOCONNECT,
+                 TRUE,
+                 NM_SETTING_CONNECTION_INTERFACE_NAME,
+                 "eth0",
+                 NM_SETTING_CONNECTION_TYPE,
+                 NM_SETTING_WIRED_SETTING_NAME,
+                 NULL);
+
+    /* Wired setting */
+    s_wired = (NMSettingWired *) nm_setting_wired_new();
+    nm_connection_add_setting(connection, NM_SETTING(s_wired));
+
+    /* IP4 setting */
+    s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
+    nm_connection_add_setting(connection, NM_SETTING(s_ip4));
+
+    g_object_set(s_ip4,
+                 NM_SETTING_IP_CONFIG_METHOD,
+                 NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+                 NM_SETTING_IP_CONFIG_GATEWAY,
+                 "1.1.1.1",
+                 NM_SETTING_IP_CONFIG_MAY_FAIL,
+                 TRUE,
+                 NULL);
+
+    addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error);
+    g_assert_no_error(error);
+    nm_setting_ip_config_add_address(s_ip4, addr);
+    nm_ip_address_unref(addr);
+
+    /* IP6 setting */
+    s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new();
+    nm_connection_add_setting(connection, NM_SETTING(s_ip6));
+
+    g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+
+    /* TC setting */
+    s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new();
+    nm_connection_add_setting(connection, NM_SETTING(s_tc));
+
+    nm_connection_add_setting(connection, nm_setting_proxy_new());
+
+    nmtst_assert_connection_verifies_without_normalization(connection);
+
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test-tc-write-empty.cexpected",
+                           &testfile);
+
+    reread = _connection_from_file(testfile, NULL, TYPE_BOND, NULL);
+
+    nmtst_assert_connection_equals(connection, FALSE, reread, FALSE);
+}
+
+static void
 test_tc_write(void)
 {
     nmtst_auto_unlinkfile char *testfile     = NULL;
@@ -11134,7 +11331,7 @@ test_tc_write(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write TC config",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -11326,6 +11523,7 @@ test_ethtool_names(void)
     } s_idxs[] = {
         {_NM_ETHTOOL_ID_FEATURE_FIRST, _NM_ETHTOOL_ID_FEATURE_LAST},
         {_NM_ETHTOOL_ID_COALESCE_FIRST, _NM_ETHTOOL_ID_COALESCE_LAST},
+        {_NM_ETHTOOL_ID_PAUSE_FIRST, _NM_ETHTOOL_ID_PAUSE_LAST},
         {_NM_ETHTOOL_ID_RING_FIRST, _NM_ETHTOOL_ID_RING_LAST},
     };
     const NMEthtoolData *data;
@@ -11520,6 +11718,8 @@ main(int argc, char **argv)
     g_test_add_func(TPATH "802-1x/password_raw", test_read_write_802_1x_password_raw);
     g_test_add_func(TPATH "802-1x/tls-p12-no-client-cert", test_read_802_1x_tls_p12_no_client_cert);
 
+    g_test_add_func(TPATH "wired/roundtrip/ethtool", test_roundtrip_ethtool);
+
     g_test_add_data_func(TPATH "wired/read/aliases/good/0",
                          GINT_TO_POINTER(0),
                          test_read_wired_aliases_good);
@@ -11626,6 +11826,7 @@ main(int argc, char **argv)
     g_test_add_func(TPATH "vlan/read/physdev", test_read_vlan_physdev);
     g_test_add_func(TPATH "vlan/read/reorder-hdr-1", test_read_vlan_reorder_hdr_1);
     g_test_add_func(TPATH "vlan/read/reorder-hdr-2", test_read_vlan_reorder_hdr_2);
+    g_test_add_func(TPATH "vlan/read/vlanid-use", test_read_vlan_vlanid_use);
     g_test_add_func(TPATH "wired/read/read-wake-on-lan", test_read_wired_wake_on_lan);
     g_test_add_func(TPATH "wired/read/read-auto-negotiate-off", test_read_wired_auto_negotiate_off);
     g_test_add_func(TPATH "wired/read/read-auto-negotiate-on", test_read_wired_auto_negotiate_on);
@@ -11848,6 +12049,7 @@ main(int argc, char **argv)
 
     g_test_add_func(TPATH "tc/read", test_tc_read);
     g_test_add_func(TPATH "tc/write", test_tc_write);
+    g_test_add_func(TPATH "tc/write_empty", test_tc_write_empty);
     g_test_add_func(TPATH "utils/test_well_known_keys", test_well_known_keys);
     g_test_add_func(TPATH "utils/test_utils_has_route_file_new_syntax",
                     test_utils_has_route_file_new_syntax);
diff --git a/src/core/settings/plugins/ifupdown/meson.build b/src/core/settings/plugins/ifupdown/meson.build
index dd252783..1bff8155 100644
--- a/src/core/settings/plugins/ifupdown/meson.build
+++ b/src/core/settings/plugins/ifupdown/meson.build
@@ -7,14 +7,12 @@ libnms_ifupdown_core = static_library(
     'nms-ifupdown-parser.c',
   ),
   dependencies: core_default_dep,
-  c_args: daemon_c_flags,
 )
 
 libnm_settings_plugin_ifupdown = shared_module(
   'nm-settings-plugin-ifupdown',
   sources: 'nms-ifupdown-plugin.c',
   dependencies: core_plugin_dep,
-  c_args: daemon_c_flags,
   link_with: libnms_ifupdown_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c
index a4974a96..db6b1aef 100644
--- a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -12,7 +12,8 @@
 #include <stdlib.h>
 #include <ctype.h>
 
-#include "nm-core-internal.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "settings/nm-settings-plugin.h"
 
 #include "nms-ifupdown-plugin.h"
@@ -32,14 +33,6 @@
 
 /*****************************************************************************/
 
-#define _str_has_prefix(val, prefix, require_suffix)                   \
-    ({                                                                 \
-        const char *_val = (val);                                      \
-                                                                       \
-        (strncmp(_val, "" prefix "", NM_STRLEN(prefix)) == 0)          \
-            && (!(require_suffix) || _val[NM_STRLEN(prefix)] != '\0'); \
-    })
-
 static const char *
 _ifupdownplugin_guess_connection_type(if_block *block)
 {
@@ -51,8 +44,7 @@ _ifupdownplugin_guess_connection_type(if_block *block)
         if_data *ifb;
 
         c_list_for_each_entry (ifb, &block->data_lst_head, data_lst) {
-            if (_str_has_prefix(ifb->key, "wireless-", FALSE)
-                || _str_has_prefix(ifb->key, "wpa-", FALSE)) {
+            if (NM_STR_HAS_PREFIX(ifb->key, "wireless-") || NM_STR_HAS_PREFIX(ifb->key, "wpa-")) {
                 ret_type = NM_SETTING_WIRELESS_SETTING_NAME;
                 break;
             }
@@ -102,7 +94,7 @@ update_wireless_setting_from_if_block(NMConnection *connection, if_block *block)
     wireless_setting = NM_SETTING_WIRELESS(nm_setting_wireless_new());
 
     c_list_for_each_entry (curr, &block->data_lst_head, data_lst) {
-        if (_str_has_prefix(curr->key, "wireless-", TRUE)) {
+        if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wireless-")) {
             const char *newkey = map_by_mapping(mapping, curr->key + NM_STRLEN("wireless-"));
 
             _LOGI("wireless setting key: %s='%s'", newkey, curr->data);
@@ -137,7 +129,7 @@ update_wireless_setting_from_if_block(NMConnection *connection, if_block *block)
             } else {
                 g_object_set(wireless_setting, newkey, curr->data, NULL);
             }
-        } else if (_str_has_prefix(curr->key, "wpa-", TRUE)) {
+        } else if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wpa-")) {
             const char *newkey = map_by_mapping(mapping, curr->key + NM_STRLEN("wpa-"));
 
             if (nm_streq0(newkey, "ssid")) {
@@ -304,7 +296,7 @@ update_wireless_security_setting_from_if_block(NMConnection *connection, if_bloc
     wireless_security_setting = NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new());
 
     c_list_for_each_entry (curr, &block->data_lst_head, data_lst) {
-        if (_str_has_prefix(curr->key, "wireless-", TRUE)) {
+        if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wireless-")) {
             const char *          key                  = curr->key + NM_STRLEN("wireless-");
             char *                property_value       = NULL;
             gpointer              typed_property_value = NULL;
@@ -336,7 +328,7 @@ wireless_next:
             if (typed_property_value && free_func)
                 (*free_func)(typed_property_value);
 
-        } else if (_str_has_prefix(curr->key, "wpa-", TRUE)) {
+        } else if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wpa-")) {
             const char *          key                  = curr->key + NM_STRLEN("wpa-");
             char *                property_value       = NULL;
             gpointer              typed_property_value = NULL;
@@ -644,7 +636,7 @@ ifupdown_new_connection_from_if_block(if_block *block, gboolean autoconnect, GEr
     type  = _ifupdownplugin_guess_connection_type(block);
     idstr = g_strconcat("Ifupdown (", block->name, ")", NULL);
 
-    uuid = nm_utils_uuid_generate_from_string(idstr, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
+    uuid = nm_uuid_generate_from_string_str(idstr, -1, NM_UUID_TYPE_LEGACY, NULL);
     g_object_set(s_con,
                  NM_SETTING_CONNECTION_TYPE,
                  type,
@@ -655,7 +647,7 @@ ifupdown_new_connection_from_if_block(if_block *block, gboolean autoconnect, GEr
                  NM_SETTING_CONNECTION_UUID,
                  uuid,
                  NM_SETTING_CONNECTION_AUTOCONNECT,
-                 (gboolean)(!!autoconnect),
+                 (gboolean) (!!autoconnect),
                  NULL);
 
     _LOGD("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 34523f8d..80a5638a 100644
--- a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -9,7 +9,7 @@
 
 #include "nms-ifupdown-plugin.h"
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-core-utils.h"
 #include "nm-config.h"
 #include "settings/nm-settings-plugin.h"
diff --git a/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c
index 6e3eb0e7..0e8db733 100644
--- a/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -5,7 +5,7 @@
 
 #include "src/core/nm-default-daemon.h"
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "settings/plugins/ifupdown/nms-ifupdown-interface-parser.h"
 #include "settings/plugins/ifupdown/nms-ifupdown-parser.h"
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c
index 789bfc1a..e8ae1814 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -13,19 +13,20 @@
 #include <sys/types.h>
 #include <sys/time.h>
 
-#include "nm-std-aux/c-list-util.h"
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-std-aux/c-list-util.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 
 #include "nm-connection.h"
 #include "nm-setting.h"
 #include "nm-setting-connection.h"
 #include "nm-utils.h"
 #include "nm-config.h"
-#include "nm-core-internal.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 
-#include "systemd/nm-sd-utils-shared.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
 #include "settings/nm-settings-plugin.h"
 #include "settings/nm-settings-storage.h"
@@ -233,7 +234,7 @@ _read_from_file(const char * full_filename,
 
     nm_assert(!connection
               || (_nm_connection_verify(connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
-    nm_assert(!connection || nm_utils_is_uuid(nm_connection_get_uuid(connection)));
+    nm_assert(!connection || nm_uuid_is_normalized(nm_connection_get_uuid(connection)));
 
     return connection;
 }
@@ -260,7 +261,7 @@ _nm_assert_storage(gpointer plugin /* NMSKeyfilePlugin  */,
 
     uuid = nms_keyfile_storage_get_uuid(storage);
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     nm_assert(((NMSKeyfileStorage *) storage)->is_meta_data
               || !(((NMSKeyfileStorage *) storage)->u.conn_data.connection)
@@ -1106,7 +1107,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone(NMSKeyfilePlugin *  self,
     const char *                       dirname;
 
     nm_assert(NMS_IS_KEYFILE_PLUGIN(self));
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
     nm_assert(!out_storage || !*out_storage);
     nm_assert(!shadowed_storage || (set && in_memory));
 
@@ -1246,9 +1247,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin)
     /* dirname_libs are a set of read-only directories with lower priority than /etc or /run.
      * There is nothing complicated about having multiple of such directories, so dirname_libs
      * is a list (which currently only has at most one directory). */
-    priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB), FALSE);
+    priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB));
     priv->dirname_libs[1] = NULL;
-    priv->dirname_run     = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN), FALSE);
+    priv->dirname_run     = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN));
     priv->dirname_etc     = nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG,
                                                  NM_CONFIG_KEYFILE_GROUP_KEYFILE,
                                                  NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH,
@@ -1261,10 +1262,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin)
     } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') {
         /* either invalid path or unspecified. Use the default. */
         g_free(priv->dirname_etc);
-        priv->dirname_etc =
-            nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE);
+        priv->dirname_etc = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT));
     } else
-        nm_sd_utils_path_simplify(priv->dirname_etc, FALSE);
+        nm_sd_utils_path_simplify(priv->dirname_etc);
 
     /* no duplicates */
     if (NM_IN_STRSET(priv->dirname_libs[0], priv->dirname_etc, priv->dirname_run))
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-reader.c b/src/core/settings/plugins/keyfile/nms-keyfile-reader.c
index 70b047f3..8b9d982f 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -9,7 +9,7 @@
 
 #include <sys/stat.h>
 
-#include "nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 
 #include "NetworkManagerUtils.h"
 #include "nms-keyfile-utils.h"
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-storage.c b/src/core/settings/plugins/keyfile/nms-keyfile-storage.c
index 89be817a..8c526c81 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-storage.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-storage.c
@@ -7,8 +7,9 @@
 
 #include "nms-keyfile-storage.h"
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nms-keyfile-plugin.h"
 
 /*****************************************************************************/
@@ -108,7 +109,7 @@ _storage_new(NMSKeyfilePlugin *    plugin,
     NMSKeyfileStorage *self;
 
     nm_assert(NMS_IS_KEYFILE_PLUGIN(plugin));
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
     nm_assert(filename && filename[0] == '/');
 
     self = g_object_new(NMS_TYPE_KEYFILE_STORAGE,
@@ -135,7 +136,7 @@ nms_keyfile_storage_new_tombstone(NMSKeyfilePlugin *    plugin,
 {
     NMSKeyfileStorage *self;
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
     nm_assert(filename && filename[0] == '/');
     nm_assert(nms_keyfile_nmmeta_check_filename(filename, NULL));
     nm_assert(NM_IN_SET(storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, NMS_KEYFILE_STORAGE_TYPE_RUN));
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-utils.c b/src/core/settings/plugins/keyfile/nms-keyfile-utils.c
index f3dffdae..e2c68bbe 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -10,8 +10,9 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 #include "nm-utils.h"
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
@@ -30,9 +31,9 @@
 const char *
 nms_keyfile_nmmeta_check_filename(const char *filename, guint *out_uuid_len)
 {
-    const char *uuid;
     const char *s;
     gsize       len;
+    char        uuid[37];
 
     s = strrchr(filename, '/');
     if (s)
@@ -50,17 +51,18 @@ nms_keyfile_nmmeta_check_filename(const char *filename, guint *out_uuid_len)
 
     len -= NM_STRLEN(NM_KEYFILE_PATH_SUFFIX_NMMETA);
 
-    if (!NM_IN_SET(len, 36, 40)) {
+    if (len != 36) {
         /* the remaining part of the filename has not the right length to
-         * contain a UUID (according to nm_utils_is_uuid()). */
+         * contain a UUID (according to nm_uuid_is_normalized()). */
         return NULL;
     }
 
-    uuid = nm_strndup_a(100, filename, len, NULL);
-    if (!nm_utils_is_uuid(uuid))
+    memcpy(uuid, filename, 36);
+    uuid[36] = '\0';
+    if (!nm_uuid_is_normalized(uuid))
         return NULL;
 
-    NM_SET_OUT(out_uuid_len, len);
+    NM_SET_OUT(out_uuid_len, 36);
     return filename;
 }
 
@@ -71,7 +73,7 @@ nms_keyfile_nmmeta_filename(const char *dirname, const char *uuid, gboolean temp
     char *s;
 
     nm_assert(dirname && dirname[0] == '/');
-    nm_assert(nm_utils_is_uuid(uuid) && !strchr(uuid, '/'));
+    nm_assert(nm_uuid_is_normalized(uuid) && !strchr(uuid, '/'));
 
     if (g_snprintf(filename,
                    sizeof(filename),
@@ -80,7 +82,7 @@ nms_keyfile_nmmeta_filename(const char *dirname, const char *uuid, gboolean temp
                    NM_KEYFILE_PATH_SUFFIX_NMMETA,
                    temporary ? "~" : "")
         >= sizeof(filename)) {
-        /* valid uuids are limited in length (nm_utils_is_uuid). The buffer should always
+        /* valid uuids are limited in length (nm_uuid_is_normalized). The buffer should always
          * be large enough. */
         nm_assert_not_reached();
     }
@@ -137,7 +139,11 @@ nms_keyfile_nmmeta_read(const char * dirname,
                                        NMMETA_KF_GROUP_NAME_NMMETA,
                                        NMMETA_KF_KEY_NAME_NMMETA_UUID,
                                        NULL);
-        if (!nm_streq0(v_uuid, uuid))
+        if (!v_uuid)
+            return FALSE;
+        if (strncmp(v_uuid, uuid, uuid_len) != 0)
+            return FALSE;
+        if (v_uuid[uuid_len] != '\0')
             return FALSE;
 
         loaded_path      = g_key_file_get_string(kf,
@@ -211,7 +217,7 @@ nms_keyfile_nmmeta_write(const char *dirname,
     int           errsv;
 
     nm_assert(dirname && dirname[0] == '/');
-    nm_assert(nm_utils_is_uuid(uuid) && !strchr(uuid, '/'));
+    nm_assert(nm_uuid_is_normalized(uuid) && !strchr(uuid, '/'));
     nm_assert(!loaded_path || loaded_path[0] == '/');
     nm_assert(!shadowed_storage || loaded_path);
 
@@ -269,7 +275,13 @@ nms_keyfile_nmmeta_write(const char *dirname,
 
         contents = g_key_file_to_data(kf, &length, NULL);
 
-        if (!nm_utils_file_set_contents(full_filename, contents, length, 0600, &errsv, NULL)) {
+        if (!nm_utils_file_set_contents(full_filename,
+                                        contents,
+                                        length,
+                                        0600,
+                                        NULL,
+                                        &errsv,
+                                        NULL)) {
             NM_SET_OUT(out_full_filename, g_steal_pointer(&full_filename_tmp));
             return -NM_ERRNO_NATIVE(errsv);
         }
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-writer.c b/src/core/settings/plugins/keyfile/nms-keyfile-writer.c
index 0e2c94b5..70afdc23 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -12,12 +12,12 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 
 #include "nms-keyfile-utils.h"
 #include "nms-keyfile-reader.h"
 
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 
 /*****************************************************************************/
 
@@ -125,13 +125,14 @@ cert_writer(NMConnection *                    connection,
 
         /* FIXME(keyfile-parse-in-memory): writer must not access/write to the file system before
          * being sure that the entire profile can be written and all circumstances are good to
-         * proceed. That means, while writing we must only collect the blogs in-memory, and write
+         * proceed. That means, while writing we must only collect the blobs in-memory, and write
          * them all in the end together (or not at all). */
         success = nm_utils_file_set_contents(new_path,
                                              (const char *) blob_data,
                                              blob_len,
                                              0600,
                                              NULL,
+                                             NULL,
                                              &local);
         if (success) {
             /* Write the path value to the keyfile.
@@ -378,7 +379,7 @@ _internal_write_connection(NMConnection *                  connection,
         }
     }
 
-    nm_utils_file_set_contents(path, kf_content_buf, kf_content_len, 0600, NULL, &local_err);
+    nm_utils_file_set_contents(path, kf_content_buf, kf_content_len, 0600, NULL, NULL, &local_err);
     if (local_err) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
diff --git a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
index 348b0ed6..a13c2586 100644
--- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -15,7 +15,8 @@
 #include <linux/if_ether.h>
 #include <linux/if_infiniband.h>
 
-#include "nm-core-internal.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "settings/plugins/keyfile/nms-keyfile-reader.h"
 #include "settings/plugins/keyfile/nms-keyfile-writer.h"
@@ -2289,7 +2290,7 @@ test_read_missing_id_uuid(void)
     gs_free char *                expected_uuid = NULL;
     const char *                  FILENAME      = TEST_KEYFILES_DIR "/Test_Missing_ID_UUID";
 
-    expected_uuid = _nm_utils_uuid_generate_from_strings("keyfile", FILENAME, NULL);
+    expected_uuid = nm_uuid_generate_from_strings("keyfile", FILENAME, NULL);
 
     connection = keyfile_read_connection_from_file(FILENAME);
 
diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c
index eab494b0..1959a161 100644
--- a/src/core/supplicant/nm-supplicant-config.c
+++ b/src/core/supplicant/nm-supplicant-config.c
@@ -10,11 +10,11 @@
 
 #include <stdlib.h>
 
-#include "nm-glib-aux/nm-str-buf.h"
-#include "nm-core-internal.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-supplicant-settings-verify.h"
 #include "nm-setting.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-setting-ip4-config.h"
@@ -334,11 +334,10 @@ nm_supplicant_config_to_variant(NMSupplicantConfig *self)
             break;
         case NM_SUPPL_OPT_TYPE_BYTES:
         case NM_SUPPL_OPT_TYPE_UTF8:
-            g_variant_builder_add(
-                &builder,
-                "{sv}",
-                key,
-                g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, option->value, option->len, 1));
+            g_variant_builder_add(&builder,
+                                  "{sv}",
+                                  key,
+                                  nm_g_variant_new_ay((const guint8 *) option->value, option->len));
             break;
         case NM_SUPPL_OPT_TYPE_KEYWORD:
         case NM_SUPPL_OPT_TYPE_STRING:
@@ -667,8 +666,8 @@ add_string_val(NMSupplicantConfig *self,
                             display_value,                                                \
                             error)                                                        \
     ({                                                                                    \
-        typeof(*(setting)) *_setting = (setting);                                         \
-        gboolean            _success = TRUE;                                              \
+        typeof(setting) _setting = (setting);                                             \
+        gboolean        _success = TRUE;                                                  \
                                                                                           \
         if (nm_setting_##setting_name##_get_num_##field_plural(_setting)) {               \
             const char _separator = (separator);                                          \
@@ -815,7 +814,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig *
     nm_auto_free_gstring GString *key_mgmt_conf = NULL;
     const char *                  key_mgmt, *auth_alg;
     const char *                  psk;
-    gboolean                      set_pmf;
+    gboolean                      set_pmf, wps_disabled;
 
     g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE);
     g_return_val_if_fail(setting != NULL, FALSE);
@@ -835,53 +834,82 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig *
     }
 
     key_mgmt      = nm_setting_wireless_security_get_key_mgmt(setting);
-    key_mgmt_conf = g_string_new(key_mgmt);
-    if (nm_streq(key_mgmt, "wpa-psk")) {
+    key_mgmt_conf = g_string_new("");
+
+    if (nm_streq(key_mgmt, "none")) {
+        g_string_append(key_mgmt_conf, "NONE");
+
+    } else if (nm_streq(key_mgmt, "ieee8021x")) {
+        g_string_append(key_mgmt_conf, "IEEE8021X");
+
+    } else if (nm_streq(key_mgmt, "owe")) {
+        pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED;
+
+        g_string_append(key_mgmt_conf, "OWE");
+
+    } else if (nm_streq(key_mgmt, "wpa-psk")) {
+        if (pmf != NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)
+            g_string_append(key_mgmt_conf, "WPA-PSK");
         if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF))
-            g_string_append(key_mgmt_conf, " wpa-psk-sha256");
+            g_string_append(key_mgmt_conf, " WPA-PSK-SHA256");
         if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
-            g_string_append(key_mgmt_conf, " ft-psk");
+            g_string_append(key_mgmt_conf, " FT-PSK");
+        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SAE)) {
+            g_string_append(key_mgmt_conf, " SAE");
+            if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
+                g_string_append(key_mgmt_conf, " FT-SAE");
+        }
+
+    } else if (nm_streq(key_mgmt, "sae")) {
+        pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED;
+
+        g_string_append(key_mgmt_conf, "SAE");
+        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
+            g_string_append(key_mgmt_conf, " FT-SAE");
+
     } else if (nm_streq(key_mgmt, "wpa-eap")) {
+        if (pmf != NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)
+            g_string_append(key_mgmt_conf, "WPA-EAP");
+        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) {
+            g_string_append(key_mgmt_conf, " FT-EAP");
+            if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
+                g_string_append(key_mgmt_conf, " FT-EAP-SHA384");
+        }
         if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) {
-            g_string_append(key_mgmt_conf, " wpa-eap-sha256");
-
+            g_string_append(key_mgmt_conf, " WPA-EAP-SHA256");
             if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SUITEB192)
                 && pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)
-                g_string_append(key_mgmt_conf, " wpa-eap-suite-b-192");
+                g_string_append(key_mgmt_conf, " WPA-EAP-SUITE-B-192");
         }
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
-            g_string_append(key_mgmt_conf, " ft-eap");
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)
-            && _get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
-            g_string_append(key_mgmt_conf, " ft-eap-sha384");
+
         switch (fils) {
         case NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED:
             g_string_truncate(key_mgmt_conf, 0);
             if (!_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF))
-                g_string_assign(key_mgmt_conf, "fils-sha256 fils-sha384");
+                g_string_assign(key_mgmt_conf, "FILS-SHA256 FILS-SHA384");
             /* fall-through */
         case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL:
-            if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF))
-                g_string_append(key_mgmt_conf, " fils-sha256 fils-sha384");
-            if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)
-                && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
-                g_string_append(key_mgmt_conf, " ft-fils-sha256");
-            if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)
-                && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)
-                && _get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
-                g_string_append(key_mgmt_conf, " ft-fils-sha384");
+            if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) {
+                g_string_append(key_mgmt_conf, " FILS-SHA256 FILS-SHA384");
+                if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) {
+                    g_string_append(key_mgmt_conf, " FT-FILS-SHA256");
+                    if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
+                        g_string_append(key_mgmt_conf, " FT-FILS-SHA384");
+                }
+            }
             break;
+
         default:
             break;
         }
-    } else if (nm_streq(key_mgmt, "sae")) {
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
-            g_string_append(key_mgmt_conf, " ft-sae");
+
     } else if (nm_streq(key_mgmt, "wpa-eap-suite-b-192")) {
         pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED;
-        if (!nm_supplicant_config_add_option(self, "pairwise", "GCMP-256", -1, NULL, error)
-            || !nm_supplicant_config_add_option(self, "group", "GCMP-256", -1, NULL, error))
-            return FALSE;
+
+        g_string_append(key_mgmt_conf, "WPA-EAP-SUITE-B-192");
+        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)
+            && _get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
+            g_string_append(key_mgmt_conf, " FT-EAP-SHA384");
     }
 
     if (!add_string_val(self, key_mgmt_conf->str, "key_mgmt", TRUE, NULL, error))
@@ -950,10 +978,6 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig *
         }
     }
 
-    /* Don't try to enable PMF on non-WPA/SAE/OWE networks */
-    if (!NM_IN_STRSET(key_mgmt, "wpa-eap", "wpa-eap-suite-b-192", "wpa-psk", "sae", "owe"))
-        pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE;
-
     /* Check if we actually support PMF */
     set_pmf = TRUE;
     if (!_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) {
@@ -968,7 +992,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig *
     }
 
     /* Only WPA-specific things when using WPA */
-    if (NM_IN_STRSET(key_mgmt, "wpa-psk", "wpa-eap", "sae", "owe")) {
+    if (NM_IN_STRSET(key_mgmt, "owe", "wpa-psk", "sae", "wpa-eap", "wpa-eap-suite-b-192")) {
         if (!ADD_STRING_LIST_VAL(self,
                                  setting,
                                  wireless_security,
@@ -980,29 +1004,40 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig *
                                  NULL,
                                  error))
             return FALSE;
-        if (!ADD_STRING_LIST_VAL(self,
-                                 setting,
-                                 wireless_security,
-                                 pairwise,
-                                 pairwise,
-                                 "pairwise",
-                                 ' ',
-                                 TRUE,
-                                 NULL,
-                                 error))
-            return FALSE;
-        if (!ADD_STRING_LIST_VAL(self,
-                                 setting,
-                                 wireless_security,
-                                 group,
-                                 groups,
-                                 "group",
-                                 ' ',
-                                 TRUE,
-                                 NULL,
-                                 error))
-            return FALSE;
 
+        if (nm_streq(key_mgmt, "wpa-eap-suite-b-192")) {
+            if (!nm_supplicant_config_add_option(self, "pairwise", "GCMP-256", -1, NULL, error))
+                return FALSE;
+            if (!nm_supplicant_config_add_option(self, "group", "GCMP-256", -1, NULL, error))
+                return FALSE;
+        } else {
+            if (!ADD_STRING_LIST_VAL(self,
+                                     setting,
+                                     wireless_security,
+                                     pairwise,
+                                     pairwise,
+                                     "pairwise",
+                                     ' ',
+                                     TRUE,
+                                     NULL,
+                                     error))
+                return FALSE;
+            if (!ADD_STRING_LIST_VAL(self,
+                                     setting,
+                                     wireless_security,
+                                     group,
+                                     groups,
+                                     "group",
+                                     ' ',
+                                     TRUE,
+                                     NULL,
+                                     error))
+                return FALSE;
+        }
+
+        /* We set the supplicants global "pmf" config value to "1" (optional),
+         * so no need to set it network-specific again if PMF_OPTIONAL is set.
+         */
         if (set_pmf
             && NM_IN_SET(pmf,
                          NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE,
@@ -1102,6 +1137,13 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig *
         }
     }
 
+    wps_disabled = (nm_setting_wireless_security_get_wps_method(setting)
+                    == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED);
+    if (wps_disabled) {
+        if (!nm_supplicant_config_add_option(self, "wps_disabled", "1", 1, NULL, error))
+            return FALSE;
+    }
+
     return TRUE;
 }
 
diff --git a/src/core/supplicant/nm-supplicant-interface.c b/src/core/supplicant/nm-supplicant-interface.c
index 6d9c604f..1556e290 100644
--- a/src/core/supplicant/nm-supplicant-interface.c
+++ b/src/core/supplicant/nm-supplicant-interface.c
@@ -12,13 +12,13 @@
 #include <linux/if_ether.h>
 
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-ref-string.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-ref-string.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 #include "nm-supplicant-config.h"
 #include "nm-supplicant-manager.h"
-#include "shared/nm-glib-aux/nm-dbus-aux.h"
 
 #define DBUS_TIMEOUT_MSEC 20000
 
@@ -267,30 +267,41 @@ security_from_vardict(GVariant *security)
     NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
     const char **          array;
     const char *           tmp;
+    gsize                  i;
+    const char *           v;
 
     nm_assert(g_variant_is_of_type(security, G_VARIANT_TYPE_VARDICT));
 
     if (g_variant_lookup(security, "KeyMgmt", "^a&s", &array)) {
-        if (g_strv_contains(array, "wpa-psk") || g_strv_contains(array, "wpa-ft-psk"))
-            flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
-        if (g_strv_contains(array, "wpa-eap") || g_strv_contains(array, "wpa-ft-eap")
-            || g_strv_contains(array, "wpa-fils-sha256")
-            || g_strv_contains(array, "wpa-fils-sha384"))
-            flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
-        if (g_strv_contains(array, "sae"))
-            flags |= NM_802_11_AP_SEC_KEY_MGMT_SAE;
-        if (g_strv_contains(array, "owe"))
-            flags |= NM_802_11_AP_SEC_KEY_MGMT_OWE;
-        if (g_strv_contains(array, "wpa-eap-suite-b-192"))
-            flags |= NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192;
+        for (i = 0; (v = array[i]); i++) {
+            if (NM_IN_STRSET(v, "wpa-psk", "wpa-psk-sha256", "wpa-ft-psk"))
+                flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+            else if (NM_IN_STRSET(v,
+                                  "wpa-eap",
+                                  "wpa-eap-sha256",
+                                  "wpa-ft-eap",
+                                  "wpa-fils-sha256",
+                                  "wpa-fils-sha384",
+                                  "wpa-fils-ft-sha256",
+                                  "wpa-fils-ft-sha384"))
+                flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+            else if (NM_IN_STRSET(v, "sae", "ft-sae"))
+                flags |= NM_802_11_AP_SEC_KEY_MGMT_SAE;
+            else if (NM_IN_STRSET(v, "owe"))
+                flags |= NM_802_11_AP_SEC_KEY_MGMT_OWE;
+            else if (NM_IN_STRSET(v, "wpa-eap-suite-b-192", "wpa-ft-eap-sha384"))
+                flags |= NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192;
+        }
         g_free(array);
     }
 
     if (g_variant_lookup(security, "Pairwise", "^a&s", &array)) {
-        if (g_strv_contains(array, "tkip"))
-            flags |= NM_802_11_AP_SEC_PAIR_TKIP;
-        if (g_strv_contains(array, "ccmp"))
-            flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+        for (i = 0; (v = array[i]); i++) {
+            if (NM_IN_STRSET(v, "tkip"))
+                flags |= NM_802_11_AP_SEC_PAIR_TKIP;
+            else if (NM_IN_STRSET(v, "ccmp"))
+                flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+        }
         g_free(array);
     }
 
@@ -359,8 +370,12 @@ _dbus_connection_call_simple_cb(GObject *source, GAsyncResult *result, gpointer
     gs_free_error GError *error    = NULL;
     const char *          log_reason;
     gs_free char *        remote_error = NULL;
+    gpointer              p_suppress_warning;
+    gboolean              suppress_warning;
+
+    nm_utils_user_data_unpack(user_data, &self, &log_reason, &p_suppress_warning);
 
-    nm_utils_user_data_unpack(user_data, &self, &log_reason);
+    suppress_warning = GPOINTER_TO_INT(p_suppress_warning);
 
     res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
     if (nm_utils_error_is_cancelled(error))
@@ -371,37 +386,70 @@ _dbus_connection_call_simple_cb(GObject *source, GAsyncResult *result, gpointer
         return;
     }
 
-    remote_error = g_dbus_error_get_remote_error(error);
-    if (!nm_streq0(remote_error, "fi.w1.wpa_supplicant1.NotConnected")) {
-        g_dbus_error_strip_remote_error(error);
-        _LOGW("call-%s: failed with %s", log_reason, error->message);
-        return;
+    if (!suppress_warning) {
+        remote_error = g_dbus_error_get_remote_error(error);
+        if (!nm_streq0(remote_error, "fi.w1.wpa_supplicant1.NotConnected")) {
+            g_dbus_error_strip_remote_error(error);
+            _LOGW("call-%s: failed with %s", log_reason, error->message);
+            return;
+        }
     }
 
     _LOGT("call-%s: failed with %s", log_reason, error->message);
 }
 
 static void
-_dbus_connection_call_simple(NMSupplicantInterface *self,
-                             const char *           interface_name,
-                             const char *           method_name,
-                             GVariant *             parameters,
-                             const GVariantType *   reply_type,
-                             const char *           log_reason)
+_dbus_connection_call_simple_full_impl(NMSupplicantInterface *self,
+                                       const char *           interface_name,
+                                       const char *           method_name,
+                                       GVariant *             parameters,
+                                       const GVariantType *   reply_type,
+                                       const char *           log_reason,
+                                       gboolean               suppress_warning)
 {
     NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self);
 
-    _dbus_connection_call(self,
-                          interface_name,
-                          method_name,
-                          parameters,
-                          reply_type,
-                          G_DBUS_CALL_FLAGS_NONE,
-                          DBUS_TIMEOUT_MSEC,
-                          priv->main_cancellable,
-                          _dbus_connection_call_simple_cb,
-                          nm_utils_user_data_pack(self, log_reason));
-}
+    _dbus_connection_call(
+        self,
+        interface_name,
+        method_name,
+        parameters,
+        reply_type,
+        G_DBUS_CALL_FLAGS_NONE,
+        DBUS_TIMEOUT_MSEC,
+        priv->main_cancellable,
+        _dbus_connection_call_simple_cb,
+        nm_utils_user_data_pack(self, log_reason, GINT_TO_POINTER(suppress_warning)));
+}
+
+#define _dbus_connection_call_simple_full(self,              \
+                                          interface_name,    \
+                                          method_name,       \
+                                          parameters,        \
+                                          reply_type,        \
+                                          log_reason,        \
+                                          suppress_warning)  \
+    _dbus_connection_call_simple_full_impl((self),           \
+                                           (interface_name), \
+                                           (method_name),    \
+                                           (parameters),     \
+                                           (reply_type),     \
+                                           "" log_reason "", \
+                                           (suppress_warning))
+
+#define _dbus_connection_call_simple(self,                   \
+                                     interface_name,         \
+                                     method_name,            \
+                                     parameters,             \
+                                     reply_type,             \
+                                     log_reason)             \
+    _dbus_connection_call_simple_full_impl((self),           \
+                                           (interface_name), \
+                                           (method_name),    \
+                                           (parameters),     \
+                                           (reply_type),     \
+                                           "" log_reason "", \
+                                           FALSE)
 
 /*****************************************************************************/
 
@@ -557,7 +605,7 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
     guint16        v_u16;
     guint32        v_u32;
     NM80211ApFlags p_ap_flags;
-    NM80211Mode    p_mode;
+    _NM80211Mode   p_mode;
     guint8         p_signal_percent;
     const guint8 * arr_data;
     gsize          arr_len;
@@ -586,7 +634,7 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
         if (v_v) {
             if (g_variant_lookup(v_v, "Type", "&s", &v_s)) {
                 f = NM_802_11_AP_FLAGS_WPS;
-                if (nm_streq(v_s, "pcb"))
+                if (nm_streq(v_s, "pbc"))
                     f |= NM_802_11_AP_FLAGS_WPS_PBC;
                 else if (nm_streq(v_s, "pin"))
                     f |= NM_802_11_AP_FLAGS_WPS_PIN;
@@ -605,15 +653,15 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
 
     if (nm_g_variant_lookup(properties, "Mode", "&s", &v_s)) {
         if (nm_streq(v_s, "infrastructure"))
-            p_mode = NM_802_11_MODE_INFRA;
+            p_mode = _NM_802_11_MODE_INFRA;
         else if (nm_streq(v_s, "ad-hoc"))
-            p_mode = NM_802_11_MODE_ADHOC;
+            p_mode = _NM_802_11_MODE_ADHOC;
         else if (nm_streq(v_s, "mesh"))
-            p_mode = NM_802_11_MODE_MESH;
+            p_mode = _NM_802_11_MODE_MESH;
         else
-            p_mode = NM_802_11_MODE_UNKNOWN;
+            p_mode = _NM_802_11_MODE_UNKNOWN;
     } else if (initial)
-        p_mode = NM_802_11_MODE_UNKNOWN;
+        p_mode = _NM_802_11_MODE_UNKNOWN;
     else
         p_mode = bss_info->mode;
     if (bss_info->mode != p_mode) {
@@ -1171,19 +1219,24 @@ parse_capabilities(NMSupplicantInterface *self, GVariant *capabilities)
     const gboolean                old_prop_scan_ssid   = priv->prop_scan_ssid;
     const guint32                 old_max_scan_ssids   = priv->max_scan_ssids;
     gboolean                      have_ft              = FALSE;
+    gboolean                      have_sae             = FALSE;
     gint32                        max_scan_ssids;
     const char **                 array;
 
     nm_assert(capabilities && g_variant_is_of_type(capabilities, G_VARIANT_TYPE_VARDICT));
 
     if (g_variant_lookup(capabilities, "KeyMgmt", "^a&s", &array)) {
-        have_ft = g_strv_contains(array, "wpa-ft-psk");
+        have_ft  = g_strv_contains(array, "wpa-ft-psk");
+        have_sae = g_strv_contains(array, "sae");
         g_free(array);
     }
 
     priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities,
                                                      NM_SUPPL_CAP_TYPE_FT,
                                                      have_ft ? NM_TERNARY_TRUE : NM_TERNARY_FALSE);
+    priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities,
+                                                     NM_SUPPL_CAP_TYPE_SAE,
+                                                     have_sae ? NM_TERNARY_TRUE : NM_TERNARY_FALSE);
 
     if (g_variant_lookup(capabilities, "Modes", "^a&s", &array)) {
         /* Setting p2p_capable might toggle _prop_p2p_available_get(). However,
@@ -1255,6 +1308,15 @@ _starting_check_ready(NMSupplicantInterface *self)
         return;
     }
 
+    _LOGD("interface supported features:"
+          " AP%c"
+          " FT%c"
+          " SAE%c"
+          "",
+          NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_AP),
+          NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_FT),
+          NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_SAE));
+
     set_state(self, priv->supp_state);
 }
 
@@ -1278,6 +1340,10 @@ _get_capability(NMSupplicantInterfacePrivate *priv, NMSupplCapType type)
                 value = iface_value;
         }
         break;
+    case NM_SUPPL_CAP_TYPE_SAE:
+        nm_assert(NM_SUPPL_CAP_MASK_GET(priv->global_capabilities, type) == NM_TERNARY_DEFAULT);
+        value = NM_SUPPL_CAP_MASK_GET(priv->iface_capabilities, type);
+        break;
     default:
         nm_assert(NM_SUPPL_CAP_MASK_GET(priv->iface_capabilities, type) == NM_TERNARY_DEFAULT);
         value = NM_SUPPL_CAP_MASK_GET(priv->global_capabilities, type);
@@ -1305,9 +1371,13 @@ nm_supplicant_interface_get_capabilities(NMSupplicantInterface *self)
     caps = NM_SUPPL_CAP_MASK_SET(caps,
                                  NM_SUPPL_CAP_TYPE_FT,
                                  _get_capability(priv, NM_SUPPL_CAP_TYPE_FT));
+    caps = NM_SUPPL_CAP_MASK_SET(caps,
+                                 NM_SUPPL_CAP_TYPE_SAE,
+                                 _get_capability(priv, NM_SUPPL_CAP_TYPE_SAE));
 
     nm_assert(!NM_FLAGS_ANY(priv->iface_capabilities,
-                            ~(NM_SUPPL_CAP_MASK_T_AP_MASK | NM_SUPPL_CAP_MASK_T_FT_MASK)));
+                            ~(NM_SUPPL_CAP_MASK_T_AP_MASK | NM_SUPPL_CAP_MASK_T_FT_MASK
+                              | NM_SUPPL_CAP_MASK_T_SAE_MASK)));
 
 #if NM_MORE_ASSERTS > 10
     {
@@ -1597,11 +1667,10 @@ _wps_handle_set_pc_cb(GVariant *res, GError *error, gpointer user_data)
          * enroll with any BSS in range. */
         if (!nm_utils_hwaddr_aton(wps_data->bssid, bssid_buf, sizeof(bssid_buf)))
             nm_assert_not_reached();
-        g_variant_builder_add(
-            &start_args,
-            "{sv}",
-            "Bssid",
-            g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, bssid_buf, ETH_ALEN, sizeof(guint8)));
+        g_variant_builder_add(&start_args,
+                              "{sv}",
+                              "Bssid",
+                              nm_g_variant_new_ay(bssid_buf, ETH_ALEN));
     }
 
     wps_data->needs_cancelling = TRUE;
@@ -1879,7 +1948,7 @@ _properties_changed_main(NMSupplicantInterface *self, GVariant *properties)
 
     if (nm_g_variant_lookup(properties, "CurrentBSS", "&o", &v_s)) {
         v_s = nm_dbus_path_not_empty(v_s);
-        if (!nm_ref_string_equals_str(priv->current_bss, v_s)) {
+        if (!nm_ref_string_equal_str(priv->current_bss, v_s)) {
             nm_ref_string_unref(priv->current_bss);
             priv->current_bss     = nm_ref_string_new(v_s);
             do_notify_current_bss = TRUE;
@@ -2699,12 +2768,13 @@ nm_supplicant_interface_p2p_cancel_connect(NMSupplicantInterface *self)
 {
     g_return_if_fail(NM_IS_SUPPLICANT_INTERFACE(self));
 
-    _dbus_connection_call_simple(self,
-                                 NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE,
-                                 "Cancel",
-                                 NULL,
-                                 G_VARIANT_TYPE("()"),
-                                 "p2p-cancel");
+    _dbus_connection_call_simple_full(self,
+                                      NM_WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE,
+                                      "Cancel",
+                                      NULL,
+                                      G_VARIANT_TYPE("()"),
+                                      "p2p-cancel",
+                                      TRUE);
 }
 
 void
diff --git a/src/core/supplicant/nm-supplicant-manager.c b/src/core/supplicant/nm-supplicant-manager.c
index 32554187..e432889b 100644
--- a/src/core/supplicant/nm-supplicant-manager.c
+++ b/src/core/supplicant/nm-supplicant-manager.c
@@ -8,13 +8,13 @@
 
 #include "nm-supplicant-manager.h"
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
-#include "nm-glib-aux/nm-ref-string.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-supplicant-interface.h"
 #include "nm-supplicant-types.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 /*****************************************************************************/
 
@@ -168,19 +168,6 @@ _caps_set(NMSupplicantManagerPrivate *priv, NMSupplCapType type, NMTernary value
     priv->capabilities = NM_SUPPL_CAP_MASK_SET(priv->capabilities, type, value);
 }
 
-static char
-_caps_to_char(NMSupplicantManagerPrivate *priv, NMSupplCapType type)
-{
-    NMTernary val;
-
-    val = NM_SUPPL_CAP_MASK_GET(priv->capabilities, type);
-    if (val == NM_TERNARY_TRUE)
-        return '+';
-    if (val == NM_TERNARY_FALSE)
-        return '-';
-    return '?';
-}
-
 /*****************************************************************************/
 
 static void
@@ -395,7 +382,7 @@ _create_iface_complete(NMSupplMgrCreateIfaceHandle *handle,
     nm_clear_g_cancellable(&handle->cancellable);
     nm_ref_string_unref(handle->name_owner);
 
-    nm_g_slice_free_fcn(handle);
+    nm_g_slice_free(handle);
 }
 
 static void
@@ -1008,15 +995,15 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data)
           " FAST%c"
           " WFD%c"
           "",
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_AP),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_PMF),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_FILS),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_P2P),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_FT),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_SHA384),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_MESH),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_FAST),
-          _caps_to_char(priv, NM_SUPPL_CAP_TYPE_WFD));
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_AP),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_PMF),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_FILS),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_P2P),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_FT),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_SHA384),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_MESH),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_FAST),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_WFD));
 
     nm_assert(g_hash_table_size(priv->supp_ifaces) == 0);
     nm_assert(c_list_is_empty(&priv->supp_lst_head));
diff --git a/src/core/supplicant/nm-supplicant-settings-verify.c b/src/core/supplicant/nm-supplicant-settings-verify.c
index 3f0a33e0..5e77b207 100644
--- a/src/core/supplicant/nm-supplicant-settings-verify.c
+++ b/src/core/supplicant/nm-supplicant-settings-verify.c
@@ -144,6 +144,7 @@ static const struct Opt opt_table[] = {
     OPT_BYTES("wep_key2", 0),
     OPT_BYTES("wep_key3", 0),
     OPT_INT("wep_tx_keyidx", 0, 3),
+    OPT_INT("wps_disabled", 0, 1),
 };
 
 static gboolean
diff --git a/src/core/supplicant/nm-supplicant-types.h b/src/core/supplicant/nm-supplicant-types.h
index adcf02db..c9b72de1 100644
--- a/src/core/supplicant/nm-supplicant-types.h
+++ b/src/core/supplicant/nm-supplicant-types.h
@@ -7,6 +7,7 @@
 #define __NETWORKMANAGER_SUPPLICANT_TYPES_H__
 
 #include "c-list/src/c-list.h"
+#include "libnm-base/nm-base.h"
 
 #define NM_WPAS_DBUS_SERVICE   "fi.w1.wpa_supplicant1"
 #define NM_WPAS_DBUS_PATH      "/fi/w1/wpa_supplicant1"
@@ -41,6 +42,7 @@ typedef enum {
     NM_SUPPL_CAP_TYPE_FILS,
     NM_SUPPL_CAP_TYPE_P2P,
     NM_SUPPL_CAP_TYPE_FT,
+    NM_SUPPL_CAP_TYPE_SAE,
     NM_SUPPL_CAP_TYPE_SHA384,
     NM_SUPPL_CAP_TYPE_MESH,
     NM_SUPPL_CAP_TYPE_FAST,
@@ -49,9 +51,9 @@ typedef enum {
     _NM_SUPPL_CAP_TYPE_NUM,
 } NMSupplCapType;
 
-#define NM_SUPPL_CAP_MASK_NO(type)   ((NMSupplCapMask)(1llu << ((type) *2u)))
-#define NM_SUPPL_CAP_MASK_YES(type)  ((NMSupplCapMask)(2llu << ((type) *2u)))
-#define NM_SUPPL_CAP_MASK_MASK(type) ((NMSupplCapMask)(3llu << ((type) *2u)))
+#define NM_SUPPL_CAP_MASK_NO(type)   ((NMSupplCapMask) (1llu << ((type) *2u)))
+#define NM_SUPPL_CAP_MASK_YES(type)  ((NMSupplCapMask) (2llu << ((type) *2u)))
+#define NM_SUPPL_CAP_MASK_MASK(type) ((NMSupplCapMask) (3llu << ((type) *2u)))
 
 typedef enum {
     NM_SUPPL_CAP_MASK_NONE = 0,
@@ -71,6 +73,7 @@ typedef enum {
     _NM_SUPPL_CAP_MASK_DEFINE(MESH),
     _NM_SUPPL_CAP_MASK_DEFINE(WFD),
     _NM_SUPPL_CAP_MASK_DEFINE(FT),
+    _NM_SUPPL_CAP_MASK_DEFINE(SAE),
     _NM_SUPPL_CAP_MASK_DEFINE(SHA384),
 #undef _NM_SUPPL_CAP_MASK_DEFINE
 } NMSupplCapMask;
@@ -111,7 +114,20 @@ NM_SUPPL_CAP_MASK_GET(NMSupplCapMask features, NMSupplCapType type)
 
     nm_assert(NM_IN_SET(f, 0, 1, 2));
 
-    return (NMTernary)(f - 1);
+    return (NMTernary) (f - 1);
+}
+
+static inline char
+NM_SUPPL_CAP_TO_CHAR(NMSupplCapMask features, NMSupplCapType type)
+{
+    NMTernary val;
+
+    val = NM_SUPPL_CAP_MASK_GET(features, type);
+    if (val == NM_TERNARY_TRUE)
+        return '+';
+    if (val == NM_TERNARY_FALSE)
+        return '-';
+    return '?';
 }
 
 /*****************************************************************************/
@@ -163,7 +179,7 @@ typedef struct _NMSupplicantBssInfo {
 
     NM80211ApFlags ap_flags : 5;
 
-    NM80211Mode mode : 4;
+    _NM80211Mode mode : 4;
 
     bool bssid_valid : 1;
 
diff --git a/src/core/supplicant/tests/test-supplicant-config.c b/src/core/supplicant/tests/test-supplicant-config.c
index 99729c18..0cf35d0e 100644
--- a/src/core/supplicant/tests/test-supplicant-config.c
+++ b/src/core/supplicant/tests/test-supplicant-config.c
@@ -14,7 +14,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "supplicant/nm-supplicant-config.h"
 #include "supplicant/nm-supplicant-settings-verify.h"
@@ -394,7 +394,17 @@ test_wifi_wpa_psk(const char *                 detail,
     NMTST_EXPECT_NM_INFO("Config: added 'scan_ssid' value '1'*");
     NMTST_EXPECT_NM_INFO("Config: added 'bssid' value '11:22:33:44:55:66'*");
     NMTST_EXPECT_NM_INFO("Config: added 'freq_list' value *");
-    NMTST_EXPECT_NM_INFO("Config: added 'key_mgmt' value 'WPA-PSK WPA-PSK-SHA256'");
+    switch (pmf) {
+    case NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE:
+    case NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL:
+        NMTST_EXPECT_NM_INFO("Config: added 'key_mgmt' value 'WPA-PSK WPA-PSK-SHA256'");
+        break;
+    case NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED:
+        NMTST_EXPECT_NM_INFO("Config: added 'key_mgmt' value ' WPA-PSK-SHA256'");
+        break;
+    default:
+        break;
+    }
     NMTST_EXPECT_NM_INFO("Config: added 'psk' value *");
     NMTST_EXPECT_NM_INFO("Config: added 'proto' value 'WPA RSN'");
     NMTST_EXPECT_NM_INFO("Config: added 'pairwise' value 'TKIP CCMP'");
@@ -421,11 +431,22 @@ test_wifi_wpa_psk(const char *                 detail,
     validate_opt(detail, config_dict, "scan_ssid", NM_SUPPL_OPT_TYPE_INT, GINT_TO_POINTER(1));
     validate_opt(detail, config_dict, "ssid", NM_SUPPL_OPT_TYPE_BYTES, ssid);
     validate_opt(detail, config_dict, "bssid", NM_SUPPL_OPT_TYPE_KEYWORD, bssid_str);
-    validate_opt(detail,
-                 config_dict,
-                 "key_mgmt",
-                 NM_SUPPL_OPT_TYPE_KEYWORD,
-                 "WPA-PSK WPA-PSK-SHA256");
+    switch (pmf) {
+    case NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE:
+    case NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL:
+        validate_opt(detail,
+                     config_dict,
+                     "key_mgmt",
+                     NM_SUPPL_OPT_TYPE_KEYWORD,
+                     "WPA-PSK WPA-PSK-SHA256");
+        break;
+    case NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED:
+        validate_opt(detail, config_dict, "key_mgmt", NM_SUPPL_OPT_TYPE_KEYWORD, " WPA-PSK-SHA256");
+        break;
+    default:
+        break;
+    }
+
     validate_opt(detail, config_dict, "proto", NM_SUPPL_OPT_TYPE_KEYWORD, "WPA RSN");
     validate_opt(detail, config_dict, "pairwise", NM_SUPPL_OPT_TYPE_KEYWORD, "TKIP CCMP");
     validate_opt(detail, config_dict, "group", NM_SUPPL_OPT_TYPE_KEYWORD, "TKIP CCMP");
@@ -483,6 +504,7 @@ test_wifi_sae_psk(const char *psk)
     NMTST_EXPECT_NM_INFO("Config: added 'proto' value 'RSN'");
     NMTST_EXPECT_NM_INFO("Config: added 'pairwise' value 'TKIP CCMP'");
     NMTST_EXPECT_NM_INFO("Config: added 'group' value 'TKIP CCMP'");
+    NMTST_EXPECT_NM_INFO("Config: added 'ieee80211w' value '2'");
     config_dict =
         build_supplicant_config(connection,
                                 1500,
@@ -815,9 +837,10 @@ test_wifi_eap_suite_b_generation(void)
     NMTST_EXPECT_NM_INFO("Config: added 'scan_ssid' value '1'*");
     NMTST_EXPECT_NM_INFO("Config: added 'bssid' value '11:22:33:44:55:66'*");
     NMTST_EXPECT_NM_INFO("Config: added 'freq_list' value *");
+    NMTST_EXPECT_NM_INFO("Config: added 'key_mgmt' value 'WPA-EAP-SUITE-B-192'");
     NMTST_EXPECT_NM_INFO("Config: added 'pairwise' value 'GCMP-256'");
     NMTST_EXPECT_NM_INFO("Config: added 'group' value 'GCMP-256'");
-    NMTST_EXPECT_NM_INFO("Config: added 'key_mgmt' value 'WPA-EAP-SUITE-B-192'");
+    NMTST_EXPECT_NM_INFO("Config: added 'ieee80211w' value '2'");
     NMTST_EXPECT_NM_INFO("Config: added 'eap' value 'TLS'");
     NMTST_EXPECT_NM_INFO("Config: added 'fragment_size' value '1086'");
     NMTST_EXPECT_NM_INFO("Config: added 'ca_cert' value '*/test-ca-cert.pem'");
diff --git a/src/core/systemd/meson.build b/src/core/systemd/meson.build
deleted file mode 100644
index 70f6b542..00000000
--- a/src/core/systemd/meson.build
+++ /dev/null
@@ -1,62 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-incs = include_directories(
-  'sd-adapt-core',
-  'src/libsystemd-network',
-  'src/libsystemd/sd-event',
-  'src/systemd',
-)
-
-libnm_systemd_core = static_library(
-  'nm-systemd-core',
-  sources: files(
-    'src/libsystemd-network/arp-util.c',
-    'src/libsystemd-network/dhcp-identifier.c',
-    'src/libsystemd-network/dhcp-network.c',
-    'src/libsystemd-network/dhcp-option.c',
-    'src/libsystemd-network/dhcp-packet.c',
-    'src/libsystemd-network/dhcp6-network.c',
-    'src/libsystemd-network/dhcp6-option.c',
-    'src/libsystemd-network/lldp-neighbor.c',
-    'src/libsystemd-network/lldp-network.c',
-    'src/libsystemd-network/network-internal.c',
-    'src/libsystemd-network/sd-dhcp-client.c',
-    'src/libsystemd-network/sd-dhcp-lease.c',
-    'src/libsystemd-network/sd-dhcp6-client.c',
-    'src/libsystemd-network/sd-dhcp6-lease.c',
-    'src/libsystemd-network/sd-ipv4acd.c',
-    'src/libsystemd-network/sd-ipv4ll.c',
-    'src/libsystemd-network/sd-lldp.c',
-    'src/libsystemd/sd-event/event-util.c',
-    'src/libsystemd/sd-event/sd-event.c',
-    'src/libsystemd/sd-id128/id128-util.c',
-    'src/libsystemd/sd-id128/sd-id128.c',
-    'nm-sd.c',
-    'nm-sd-utils-core.c',
-    'nm-sd-utils-dhcp.c',
-    'sd-adapt-core/nm-sd-adapt-core.c',
-  ),
-  include_directories: [
-    incs,
-    src_inc,
-  ],
-  dependencies: [
-    glib_nm_default_dep,
-    libnm_core_dep,
-    libnm_systemd_shared_dep,
-  ],
-  c_args: [
-    '-DG_LOG_DOMAIN="NetworkManager"',
-  ],
-  link_with: libc_siphash,
-)
-
-libnm_systemd_core_dep = declare_dependency(
-  include_directories: incs,
-  dependencies: [
-    glib_dep,
-    libnm_core_dep,
-    libnm_systemd_shared_dep,
-  ],
-  link_with: libnm_systemd_core,
-)
diff --git a/src/core/systemd/nm-default-systemd.h b/src/core/systemd/nm-default-systemd.h
deleted file mode 100644
index 1979d9c9..00000000
--- a/src/core/systemd/nm-default-systemd.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2015 Red Hat, Inc.
- */
-
-#ifndef __NM_DEFAULT_SYSTEMD_H__
-#define __NM_DEFAULT_SYSTEMD_H__
-
-/*****************************************************************************/
-
-#include "nm-default-daemon.h"
-
-#undef NETWORKMANAGER_COMPILATION
-#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_SYSTEMD
-
-/*****************************************************************************/
-
-#endif /* __NM_DEFAULT_SYSTEMD_H__ */
diff --git a/src/core/systemd/nm-sd-utils-core.c b/src/core/systemd/nm-sd-utils-core.c
deleted file mode 100644
index af68603e..00000000
--- a/src/core/systemd/nm-sd-utils-core.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#include "src/core/systemd/nm-default-systemd.h"
-
-#include "nm-sd-utils-core.h"
-
-#include "nm-core-internal.h"
-
-#include "nm-sd-adapt-core.h"
-
-#include "sd-id128.h"
-
-/*****************************************************************************/
-
-NMUuid *
-nm_sd_utils_id128_get_machine(NMUuid *out_uuid)
-{
-    g_assert(out_uuid);
-
-    G_STATIC_ASSERT_EXPR(sizeof(*out_uuid) == sizeof(sd_id128_t));
-    if (sd_id128_get_machine((sd_id128_t *) out_uuid) < 0)
-        return NULL;
-    return out_uuid;
-}
diff --git a/src/core/systemd/nm-sd-utils-core.h b/src/core/systemd/nm-sd-utils-core.h
deleted file mode 100644
index ccad0029..00000000
--- a/src/core/systemd/nm-sd-utils-core.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2018 Red Hat, Inc.
- */
-
-#ifndef __NM_SD_UTILS_CORE_H__
-#define __NM_SD_UTILS_CORE_H__
-
-/*****************************************************************************/
-
-struct _NMUuid;
-
-struct _NMUuid *nm_sd_utils_id128_get_machine(struct _NMUuid *out_uuid);
-
-/*****************************************************************************/
-
-#endif /* __NM_SD_UTILS_CORE_H__ */
diff --git a/src/core/systemd/nm-sd-utils-dhcp.c b/src/core/systemd/nm-sd-utils-dhcp.c
deleted file mode 100644
index b5aa315a..00000000
--- a/src/core/systemd/nm-sd-utils-dhcp.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2019 Red Hat, Inc.
- */
-
-#include "src/core/systemd/nm-default-systemd.h"
-
-#include "nm-sd-utils-dhcp.h"
-
-#include "sd-adapt-core/nm-sd-adapt-core.h"
-#include "src/libsystemd-network/dhcp-lease-internal.h"
-
-int
-nm_sd_dhcp_lease_get_private_options(sd_dhcp_lease *lease, nm_sd_dhcp_option **out_options)
-{
-    struct sd_dhcp_raw_option *raw_option;
-    int                        cnt = 0;
-
-    g_return_val_if_fail(lease, -EINVAL);
-    g_return_val_if_fail(out_options, -EINVAL);
-    g_return_val_if_fail(*out_options == NULL, -EINVAL);
-
-    if (lease->private_options == NULL)
-        return -ENODATA;
-
-    LIST_FOREACH(options, raw_option, lease->private_options)
-    cnt++;
-
-    *out_options = g_new(nm_sd_dhcp_option, cnt);
-    cnt          = 0;
-
-    LIST_FOREACH(options, raw_option, lease->private_options)
-    {
-        (*out_options)[cnt].code     = raw_option->tag;
-        (*out_options)[cnt].data     = raw_option->data;
-        (*out_options)[cnt].data_len = raw_option->length;
-        cnt++;
-    }
-
-    return cnt;
-}
diff --git a/src/core/systemd/nm-sd-utils-dhcp.h b/src/core/systemd/nm-sd-utils-dhcp.h
deleted file mode 100644
index 5d126d5b..00000000
--- a/src/core/systemd/nm-sd-utils-dhcp.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2019 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_DHCP_SYSTEMD_UTILS_H__
-#define __NETWORKMANAGER_DHCP_SYSTEMD_UTILS_H__
-
-#include "nm-sd.h"
-
-typedef struct {
-    uint8_t code;
-    uint8_t data_len;
-    void *  data;
-} nm_sd_dhcp_option;
-
-int nm_sd_dhcp_lease_get_private_options(sd_dhcp_lease *lease, nm_sd_dhcp_option **out_options);
-
-#endif /* __NETWORKMANAGER_DHCP_SYSTEMD_UTILS_H__ */
diff --git a/src/core/systemd/nm-sd.c b/src/core/systemd/nm-sd.c
deleted file mode 100644
index 11f3abbd..00000000
--- a/src/core/systemd/nm-sd.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2014 - 2016 Red Hat, Inc.
- */
-
-#include "src/core/systemd/nm-default-systemd.h"
-
-#include "nm-sd.h"
-
-#include "sd-event.h"
-
-/*****************************************************************************
- * Integrating sd_event into glib. Taken and adjusted from
- * https://www.freedesktop.org/software/systemd/man/sd_event_get_fd.html
- *****************************************************************************/
-
-typedef struct SDEventSource {
-    GSource   source;
-    GPollFD   pollfd;
-    sd_event *event;
-} SDEventSource;
-
-static gboolean
-event_prepare(GSource *source, int *timeout_)
-{
-    return sd_event_prepare(((SDEventSource *) source)->event) > 0;
-}
-
-static gboolean
-event_check(GSource *source)
-{
-    return sd_event_wait(((SDEventSource *) source)->event, 0) > 0;
-}
-
-static gboolean
-event_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
-{
-    return sd_event_dispatch(((SDEventSource *) source)->event) > 0;
-}
-
-static void
-event_finalize(GSource *source)
-{
-    SDEventSource *s = (SDEventSource *) source;
-
-    sd_event_unref(s->event);
-}
-
-static SDEventSource *
-event_create_source(sd_event *event)
-{
-    static const GSourceFuncs event_funcs = {
-        .prepare  = event_prepare,
-        .check    = event_check,
-        .dispatch = event_dispatch,
-        .finalize = event_finalize,
-    };
-    SDEventSource *source;
-    gboolean       is_default_event = FALSE;
-    int            r;
-
-    if (!event) {
-        is_default_event = TRUE;
-        r                = sd_event_default(&event);
-        if (r < 0)
-            g_return_val_if_reached(NULL);
-    }
-
-    source = (SDEventSource *) g_source_new((GSourceFuncs *) &event_funcs, sizeof(SDEventSource));
-
-    source->event = is_default_event ? g_steal_pointer(&event) : sd_event_ref(event);
-
-    source->pollfd = (GPollFD){
-        .fd     = sd_event_get_fd(source->event),
-        .events = G_IO_IN | G_IO_HUP | G_IO_ERR,
-    };
-
-    g_source_add_poll(&source->source, &source->pollfd);
-
-    return source;
-}
-
-static guint
-event_attach(sd_event *event, GMainContext *context)
-{
-    SDEventSource *source;
-    guint          id;
-
-    source = event_create_source(event);
-
-    g_return_val_if_fail(source, 0);
-
-    id = g_source_attach((GSource *) source, context);
-    g_source_unref((GSource *) source);
-
-    nm_assert(id != 0);
-    return id;
-}
-
-guint
-nm_sd_event_attach_default(void)
-{
-    return event_attach(NULL, NULL);
-}
-
-/*****************************************************************************/
-
-/* ensure that defines in nm-sd.h correspond to the internal defines. */
-
-#include "nm-sd-adapt-core.h"
-#include "dhcp-lease-internal.h"
-
-/*****************************************************************************/
diff --git a/src/core/systemd/nm-sd.h b/src/core/systemd/nm-sd.h
deleted file mode 100644
index 0ea4be22..00000000
--- a/src/core/systemd/nm-sd.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2014 - 2016 Red Hat, Inc.
- */
-
-#ifndef __NM_SD_H__
-#define __NM_SD_H__
-
-#include "systemd/src/systemd/sd-dhcp-client.h"
-#include "systemd/src/systemd/sd-dhcp6-client.h"
-#include "systemd/src/systemd/sd-lldp.h"
-#include "systemd/src/systemd/sd-ipv4ll.h"
-
-/*****************************************************************************/
-
-guint nm_sd_event_attach_default(void);
-
-/*****************************************************************************
- * expose internal systemd API
- *
- * FIXME: don't use any internal systemd API.
- *****************************************************************************/
-
-struct sd_dhcp_lease;
-
-int dhcp_lease_save(struct sd_dhcp_lease *lease, const char *lease_file);
-int dhcp_lease_load(struct sd_dhcp_lease **ret, const char *lease_file);
-
-#endif /* __NM_SD_H__ */
diff --git a/src/core/systemd/sd-adapt-core/condition.h b/src/core/systemd/sd-adapt-core/condition.h
deleted file mode 100644
index d3a6812a..00000000
--- a/src/core/systemd/sd-adapt-core/condition.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-
-/* dummy header */
-
-typedef struct _sd_adapt_Condition Condition;
diff --git a/src/core/systemd/sd-adapt-core/conf-parser.h b/src/core/systemd/sd-adapt-core/conf-parser.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/core/systemd/sd-adapt-core/conf-parser.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/core/systemd/sd-adapt-core/device-util.h b/src/core/systemd/sd-adapt-core/device-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/core/systemd/sd-adapt-core/device-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/core/systemd/sd-adapt-core/khash.h b/src/core/systemd/sd-adapt-core/khash.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/core/systemd/sd-adapt-core/khash.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/core/systemd/sd-adapt-core/network-util.h b/src/core/systemd/sd-adapt-core/network-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/core/systemd/sd-adapt-core/network-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/core/systemd/sd-adapt-core/nm-sd-adapt-core.c b/src/core/systemd/sd-adapt-core/nm-sd-adapt-core.c
deleted file mode 100644
index 9339b33d..00000000
--- a/src/core/systemd/sd-adapt-core/nm-sd-adapt-core.c
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2014 - 2016 Red Hat, Inc.
- */
-
-#include "src/core/systemd/nm-default-systemd.h"
-
-#include "nm-sd-adapt-core.h"
-
-#include "fd-util.h"
-
-/*****************************************************************************/
-
-int
-asynchronous_close(int fd)
-{
-    safe_close(fd);
-    return -1;
-}
-
-/*****************************************************************************/
diff --git a/src/core/systemd/sd-adapt-core/nm-sd-adapt-core.h b/src/core/systemd/sd-adapt-core/nm-sd-adapt-core.h
deleted file mode 100644
index f627d7d4..00000000
--- a/src/core/systemd/sd-adapt-core/nm-sd-adapt-core.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2014 - 2018 Red Hat, Inc.
- */
-
-#ifndef __NM_SD_ADAPT_CORE_H__
-#define __NM_SD_ADAPT_CORE_H__
-
-#include "src/core/systemd/nm-default-systemd.h"
-
-#include <stdbool.h>
-#include <sys/resource.h>
-#include <time.h>
-
-#undef NETWORKMANAGER_COMPILATION
-#include "systemd/sd-adapt-shared/nm-sd-adapt-shared.h"
-#undef NETWORKMANAGER_COMPILATION
-#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_SYSTEMD
-
-#ifndef HAVE_SYS_AUXV_H
-    #define HAVE_SYS_AUXV_H 0
-#endif
-
-/*****************************************************************************
- * The remainder of the header is only enabled when building the systemd code
- * itself.
- *****************************************************************************/
-
-#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD
-
-    #include <netinet/in.h>
-    #include <string.h>
-    #include <stdio.h>
-    #include <errno.h>
-    #include <elf.h>
-    #ifdef HAVE_SYS_AUXV_H
-        #include <sys/auxv.h>
-    #endif
-    #include <unistd.h>
-    #include <sys/syscall.h>
-    #include <sys/ioctl.h>
-
-    /* Missing in Linux 3.2.0, in Ubuntu 12.04 */
-    #ifndef BPF_XOR
-        #define BPF_XOR 0xa0
-    #endif
-
-    #ifndef ETHERTYPE_LLDP
-        #define ETHERTYPE_LLDP 0x88cc
-    #endif
-
-    #ifndef HAVE_SECURE_GETENV
-        #ifdef HAVE___SECURE_GETENV
-            #define secure_getenv __secure_getenv
-        #else
-            #error neither secure_getenv nor __secure_getenv is available
-        #endif
-    #endif
-
-/*****************************************************************************/
-
-static inline int
-sd_notify(int unset_environment, const char *state)
-{
-    return 0;
-}
-
-    /* Can't include both net/if.h and linux/if.h; so have to define this here */
-    #ifndef IF_NAMESIZE
-        #define IF_NAMESIZE 16
-    #endif
-
-    #ifndef IFNAMSIZ
-        #define IFNAMSIZ IF_NAMESIZE
-    #endif
-
-    #ifndef MAX_HANDLE_SZ
-        #define MAX_HANDLE_SZ 128
-    #endif
-
-    #include "sd-id128.h"
-    #include "sparse-endian.h"
-    #include "async.h"
-    #include "util.h"
-
-#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */
-
-#endif /* __NM_SD_ADAPT_CORE_H__ */
diff --git a/src/core/systemd/sd-adapt-core/sd-daemon.h b/src/core/systemd/sd-adapt-core/sd-daemon.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/core/systemd/sd-adapt-core/sd-daemon.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/core/systemd/sd-adapt-core/sd-device.h b/src/core/systemd/sd-adapt-core/sd-device.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/core/systemd/sd-adapt-core/sd-device.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/core/systemd/sd-adapt-core/udev-util.h b/src/core/systemd/sd-adapt-core/udev-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/core/systemd/sd-adapt-core/udev-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/core/systemd/src/libsystemd-network/arp-util.c b/src/core/systemd/src/libsystemd-network/arp-util.c
deleted file mode 100644
index 1c777d8a..00000000
--- a/src/core/systemd/src/libsystemd-network/arp-util.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <linux/filter.h>
-#include <netinet/if_ether.h>
-
-#include "arp-util.h"
-#include "fd-util.h"
-#include "unaligned.h"
-#include "util.h"
-
-int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
-        struct sock_filter filter[] = {
-                BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),                                         /* A <- packet length */
-                BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0),           /* packet >= arp packet ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),                       /* header == ethernet ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),                       /* protocol == IP ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0),          /* length == sizeof(ether_addr)? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0),             /* length == sizeof(in_addr) ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)),  /* A <- operation */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),                      /* protocol == request ? */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),                        /* protocol == reply ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                /* Sender Hardware Address must be different from our own */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)),       /* A <- 4 bytes of SHA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6),                                  /* A == 0 ? */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4),   /* A <- remainder of SHA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
-                BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)),                                  /* A <- clients IP */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)),       /* A <- SPA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* X xor A */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 65535),                                              /* return all */
-                BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)),                                  /* A <- clients IP */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)),       /* A <- TPA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* X xor A */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 65535),                                              /* return all */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-        };
-        struct sock_fprog fprog = {
-                .len = ELEMENTSOF(filter),
-                .filter = (struct sock_filter*) filter
-        };
-        union sockaddr_union link = {
-                .ll.sll_family = AF_PACKET,
-                .ll.sll_protocol = htobe16(ETH_P_ARP),
-                .ll.sll_ifindex = ifindex,
-                .ll.sll_halen = ETH_ALEN,
-                .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
-        };
-        _cleanup_close_ int s = -1;
-        int r;
-
-        assert(ifindex > 0);
-
-        s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
-        if (s < 0)
-                return -errno;
-
-        r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
-        if (r < 0)
-                return -errno;
-
-        r = bind(s, &link.sa, sizeof(link.ll));
-        if (r < 0)
-                return -errno;
-
-        return TAKE_FD(s);
-}
-
-static int arp_send_packet(int fd, int ifindex,
-                           be32_t pa, const struct ether_addr *ha,
-                           bool announce) {
-        union sockaddr_union link = {
-                .ll.sll_family = AF_PACKET,
-                .ll.sll_protocol = htobe16(ETH_P_ARP),
-                .ll.sll_ifindex = ifindex,
-                .ll.sll_halen = ETH_ALEN,
-                .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
-        };
-        struct ether_arp arp = {
-                .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
-                .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
-                .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
-                .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
-                .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
-        };
-        int r;
-
-        assert(fd >= 0);
-        assert(pa != 0);
-        assert(ha);
-
-        memcpy(&arp.arp_sha, ha, ETH_ALEN);
-        memcpy(&arp.arp_tpa, &pa, sizeof(pa));
-
-        if (announce)
-                memcpy(&arp.arp_spa, &pa, sizeof(pa));
-
-        r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
-        if (r < 0)
-                return -errno;
-
-        return 0;
-}
-
-int arp_send_probe(int fd, int ifindex,
-                    be32_t pa, const struct ether_addr *ha) {
-        return arp_send_packet(fd, ifindex, pa, ha, false);
-}
-
-int arp_send_announcement(int fd, int ifindex,
-                          be32_t pa, const struct ether_addr *ha) {
-        return arp_send_packet(fd, ifindex, pa, ha, true);
-}
diff --git a/src/core/systemd/src/libsystemd-network/arp-util.h b/src/core/systemd/src/libsystemd-network/arp-util.h
deleted file mode 100644
index 2dac8cfb..00000000
--- a/src/core/systemd/src/libsystemd-network/arp-util.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-***/
-
-#include <net/ethernet.h>
-
-#include "socket-util.h"
-#include "sparse-endian.h"
-
-int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
-
-int arp_send_probe(int fd, int ifindex,
-                   be32_t pa, const struct ether_addr *ha);
-int arp_send_announcement(int fd, int ifindex,
-                          be32_t pa, const struct ether_addr *ha);
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-identifier.c b/src/core/systemd/src/libsystemd-network/dhcp-identifier.c
deleted file mode 100644
index 0720e381..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-identifier.c
+++ /dev/null
@@ -1,228 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <linux/if_infiniband.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-
-#include "sd-device.h"
-#include "sd-id128.h"
-
-#include "dhcp-identifier.h"
-#include "dhcp6-protocol.h"
-#include "network-util.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "stdio-util.h"
-#include "udev-util.h"
-#include "virt.h"
-
-#define HASH_KEY       SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
-#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
-#define USEC_2000       ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
-
-int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
-        struct duid d;
-
-        assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
-        if (duid_len > MAX_DUID_LEN)
-                return -EINVAL;
-
-        if (!strict)
-                /* Strict validation is not requested. We only ensure that the
-                 * DUID is not too long. */
-                return 0;
-
-        switch (duid_type) {
-        case DUID_TYPE_LLT:
-                if (duid_len <= sizeof(d.llt))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_EN:
-                if (duid_len != sizeof(d.en))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_LL:
-                if (duid_len <= sizeof(d.ll))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_UUID:
-                if (duid_len != sizeof(d.uuid))
-                        return -EINVAL;
-                break;
-        default:
-                /* accept unknown type in order to be forward compatible */
-                break;
-        }
-        return 0;
-}
-
-#if 0 /* NM_IGNORED */
-int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
-        uint16_t time_from_2000y;
-
-        assert(duid);
-        assert(len);
-        assert(addr);
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(addr_len == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
-        else
-                return -EINVAL;
-
-        if (t < USEC_2000)
-                time_from_2000y = 0;
-        else
-                time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
-
-        unaligned_write_be16(&duid->type, DUID_TYPE_LLT);
-        unaligned_write_be16(&duid->llt.htype, arp_type);
-        unaligned_write_be32(&duid->llt.time, time_from_2000y);
-        memcpy(duid->llt.haddr, addr, addr_len);
-
-        *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
-
-        return 0;
-}
-
-int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
-        assert(duid);
-        assert(len);
-        assert(addr);
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(addr_len == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
-        else
-                return -EINVAL;
-
-        unaligned_write_be16(&duid->type, DUID_TYPE_LL);
-        unaligned_write_be16(&duid->ll.htype, arp_type);
-        memcpy(duid->ll.haddr, addr, addr_len);
-
-        *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
-
-        return 0;
-}
-#endif /* NM_IGNORED */
-
-int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
-        sd_id128_t machine_id;
-        uint64_t hash;
-        int r;
-
-        assert(duid);
-        assert(len);
-
-        r = sd_id128_get_machine(&machine_id);
-        if (r < 0) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-                machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
-#else
-                return r;
-#endif
-        }
-
-        unaligned_write_be16(&duid->type, DUID_TYPE_EN);
-        unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
-
-        *len = sizeof(duid->type) + sizeof(duid->en);
-
-        /* a bit of snake-oil perhaps, but no need to expose the machine-id
-         * directly; duid->en.id might not be aligned, so we need to copy */
-        hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
-        memcpy(duid->en.id, &hash, sizeof(duid->en.id));
-
-        return 0;
-}
-
-#if 0 /* NM_IGNORED */
-int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
-        sd_id128_t machine_id;
-        int r;
-
-        assert(duid);
-        assert(len);
-
-        r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
-        if (r < 0)
-                return r;
-
-        unaligned_write_be16(&duid->type, DUID_TYPE_UUID);
-        memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
-
-        *len = sizeof(duid->type) + sizeof(machine_id);
-
-        return 0;
-}
-#endif
-
-int dhcp_identifier_set_iaid(
-                int ifindex,
-                const uint8_t *mac,
-                size_t mac_len,
-                bool legacy_unstable_byteorder,
-                void *_id) {
-#if 0 /* NM_IGNORED */
-        /* name is a pointer to memory in the sd_device struct, so must
-         * have the same scope */
-        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
-        const char *name = NULL;
-        uint64_t id;
-        uint32_t id32;
-
-        if (detect_container() <= 0) {
-                /* not in a container, udev will be around */
-                char ifindex_str[1 + DECIMAL_STR_MAX(int)];
-                int r;
-
-                xsprintf(ifindex_str, "n%d", ifindex);
-                if (sd_device_new_from_device_id(&device, ifindex_str) >= 0) {
-                        r = sd_device_get_is_initialized(device);
-                        if (r < 0)
-                                return r;
-                        if (r == 0)
-                                /* not yet ready */
-                                return -EBUSY;
-
-                        r = device_is_renaming(device);
-                        if (r < 0)
-                                return r;
-                        if (r > 0)
-                                /* device is under renaming */
-                                return -EBUSY;
-
-                        name = net_get_name_persistent(device);
-                }
-        }
-
-        if (name)
-                id = siphash24(name, strlen(name), HASH_KEY.bytes);
-        else
-                /* fall back to MAC address if no predictable name available */
-                id = siphash24(mac, mac_len, HASH_KEY.bytes);
-
-        id32 = (id & 0xffffffff) ^ (id >> 32);
-
-        if (legacy_unstable_byteorder)
-                /* for historical reasons (a bug), the bits were swapped and thus
-                 * the result was endianness dependent. Preserve that behavior. */
-                id32 = __bswap_32(id32);
-        else
-                /* the fixed behavior returns a stable byte order. Since LE is expected
-                 * to be more common, swap the bytes on LE to give the same as legacy
-                 * behavior. */
-                id32 = be32toh(id32);
-
-        unaligned_write_ne32(_id, id32);
-        return 0;
-#else /* NM_IGNORED */
-        /* for NetworkManager, we don't use this function and we should never call here.
-         * This got replaced by nm_utils_create_dhcp_iaid(). */
-        g_return_val_if_reached (-EINVAL);
-#endif /* NM_IGNORED */
-}
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-identifier.h b/src/core/systemd/src/libsystemd-network/dhcp-identifier.h
deleted file mode 100644
index e9f2ea7e..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-identifier.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-id128.h"
-
-#include "macro.h"
-#include "sparse-endian.h"
-#include "time-util.h"
-#include "unaligned.h"
-
-#define SYSTEMD_PEN    43793
-
-typedef enum DUIDType {
-        DUID_TYPE_LLT       = 1,
-        DUID_TYPE_EN        = 2,
-        DUID_TYPE_LL        = 3,
-        DUID_TYPE_UUID      = 4,
-        _DUID_TYPE_MAX,
-        _DUID_TYPE_INVALID  = -1,
-} DUIDType;
-
-/* RFC 3315 section 9.1:
- *      A DUID can be no more than 128 octets long (not including the type code).
- */
-#define MAX_DUID_LEN 128
-
-/* https://tools.ietf.org/html/rfc3315#section-9.1 */
-struct duid {
-        be16_t type;
-        union {
-                struct {
-                        /* DUID_TYPE_LLT */
-                        be16_t htype;
-                        be32_t time;
-                        uint8_t haddr[0];
-                } _packed_ llt;
-                struct {
-                        /* DUID_TYPE_EN */
-                        be32_t pen;
-                        uint8_t id[8];
-                } _packed_ en;
-                struct {
-                        /* DUID_TYPE_LL */
-                        be16_t htype;
-                        uint8_t haddr[0];
-                } _packed_ ll;
-                struct {
-                        /* DUID_TYPE_UUID */
-                        sd_id128_t uuid;
-                } _packed_ uuid;
-                struct {
-                        uint8_t data[MAX_DUID_LEN];
-                } _packed_ raw;
-        };
-} _packed_;
-
-int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict);
-int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
-int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
-int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
-int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len);
-int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, void *_id);
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-internal.h b/src/core/systemd/src/libsystemd-network/dhcp-internal.h
deleted file mode 100644
index 40e6b1f2..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-internal.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include <linux/if_packet.h>
-#include <net/ethernet.h>
-#include <stdint.h>
-
-#include "sd-dhcp-client.h"
-
-#include "dhcp-protocol.h"
-#include "socket-util.h"
-
-typedef struct sd_dhcp_option {
-        unsigned n_ref;
-
-        uint8_t option;
-        void *data;
-        size_t length;
-} sd_dhcp_option;
-
-typedef struct DHCPServerData {
-        struct in_addr *addr;
-        size_t size;
-} DHCPServerData;
-
-extern const struct hash_ops dhcp_option_hash_ops;
-
-int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid,
-                                 const uint8_t *mac_addr, size_t mac_addr_len,
-                                 const uint8_t *bcast_addr, size_t bcast_addr_len,
-                                 uint16_t arp_type, uint16_t port);
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type);
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
-                                 const void *packet, size_t len);
-int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
-                                 const void *packet, size_t len);
-
-int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload,
-                       uint8_t code, size_t optlen, const void *optval);
-
-typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
-                                const void *option, void *userdata);
-
-int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
-
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
-                      uint8_t type, uint16_t arp_type, size_t optlen,
-                      size_t *optoffset);
-
-uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
-
-void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
-                                   uint16_t source, be32_t destination_addr,
-                                   uint16_t destination, uint16_t len, int ip_service_type);
-
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port);
-
-/* If we are invoking callbacks of a dhcp-client, ensure unreffing the
- * client from the callback doesn't destroy the object we are working
- * on */
-#define DHCP_CLIENT_DONT_DESTROY(client) \
-        _cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
-
-#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
-#define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__)
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/core/systemd/src/libsystemd-network/dhcp-lease-internal.h
deleted file mode 100644
index 49392d1b..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-lease-internal.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include "sd-dhcp-client.h"
-
-#include "dhcp-internal.h"
-#include "dhcp-protocol.h"
-#include "list.h"
-#include "util.h"
-
-struct sd_dhcp_route {
-        struct in_addr dst_addr;
-        struct in_addr gw_addr;
-        unsigned char dst_prefixlen;
-
-        uint8_t option;
-};
-
-struct sd_dhcp_raw_option {
-        LIST_FIELDS(struct sd_dhcp_raw_option, options);
-
-        uint8_t tag;
-        uint8_t length;
-        void *data;
-};
-
-struct sd_dhcp_lease {
-        unsigned n_ref;
-
-        /* each 0 if unset */
-        uint32_t t1;
-        uint32_t t2;
-        uint32_t lifetime;
-
-        /* each 0 if unset */
-        be32_t address;
-        be32_t server_address;
-        be32_t next_server;
-
-        bool have_subnet_mask;
-        be32_t subnet_mask;
-
-        bool have_broadcast;
-        be32_t broadcast;
-
-        struct in_addr *router;
-        size_t router_size;
-
-        DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
-
-        struct sd_dhcp_route *static_route;
-        size_t static_route_size, static_route_allocated;
-
-        uint16_t mtu; /* 0 if unset */
-
-        char *domainname;
-        char **search_domains;
-        char *hostname;
-        char *root_path;
-
-        void *client_id;
-        size_t client_id_len;
-
-        void *vendor_specific;
-        size_t vendor_specific_len;
-
-        char *timezone;
-
-        LIST_HEAD(struct sd_dhcp_raw_option, private_options);
-};
-
-int dhcp_lease_new(sd_dhcp_lease **ret);
-
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata);
-int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains);
-int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len);
-
-int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
-
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-network.c b/src/core/systemd/src/libsystemd-network/dhcp-network.c
deleted file mode 100644
index 4477f46f..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-network.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <net/ethernet.h>
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <stdio.h>
-#include <string.h>
-#include <linux/filter.h>
-#include <linux/if_infiniband.h>
-#include <linux/if_packet.h>
-
-#include "dhcp-internal.h"
-#include "fd-util.h"
-#include "socket-util.h"
-#include "unaligned.h"
-
-static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
-                            uint32_t xid,
-                            const uint8_t *bcast_addr,
-                            size_t bcast_addr_len,
-                            const struct ether_addr *eth_mac,
-                            uint16_t arp_type, uint8_t dhcp_hlen,
-                            uint16_t port) {
-        struct sock_filter filter[] = {
-                BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),                                 /* A <- packet length */
-                BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0),         /* packet >= DHCPPacket ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),                /* IP protocol == UDP ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags */
-                BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x20),                             /* A <- A & 0x20 (More Fragments bit) */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                          /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags + Fragment offset */
-                BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x1fff),                           /* A <- A & 0x1fff (Fragment offset) */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                          /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)),    /* A <- UDP destination port */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0),                       /* UDP destination port == DHCP client port ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)),     /* A <- DHCP op */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0),                  /* op == BOOTREPLY ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)),  /* A <- DHCP header type */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0),                   /* header type == arp_type ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)),    /* A <- client identifier */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0),                        /* client identifier == xid ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)),   /* A <- MAC address length */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0),                  /* address length == dhcp_hlen ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-
-                /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
-                 * compare chaddr for ETH_ALEN bytes. */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 12),                                  /* A (the MAC address length) == ETH_ALEN ? */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),        /* A <- 4 bytes of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)),                 /* A <- 4 bytes of MAC from dhcp.chaddr */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                                          /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),        /* A <- remainder of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4),             /* A <- remainder of MAC from dhcp.chaddr */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                                          /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
-
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)),  /* A <- DHCP magic cookie */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0),          /* cookie == DHCP magic cookie ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_RET + BPF_K, 65535),                                      /* return all */
-        };
-        struct sock_fprog fprog = {
-                .len = ELEMENTSOF(filter),
-                .filter = filter
-        };
-        _cleanup_close_ int s = -1;
-        int r;
-
-        assert(ifindex > 0);
-        assert(link);
-
-        s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
-        if (s < 0)
-                return -errno;
-
-        r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true);
-        if (r < 0)
-                return r;
-
-        r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
-        if (r < 0)
-                return -errno;
-
-        link->ll = (struct sockaddr_ll) {
-                .sll_family = AF_PACKET,
-                .sll_protocol = htobe16(ETH_P_IP),
-                .sll_ifindex = ifindex,
-                .sll_hatype = htobe16(arp_type),
-                .sll_halen = bcast_addr_len,
-        };
-        memcpy(link->ll.sll_addr, bcast_addr, bcast_addr_len); /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
-
-        r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll));
-        if (r < 0)
-                return -errno;
-
-        return TAKE_FD(s);
-}
-
-int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid,
-                                 const uint8_t *mac_addr, size_t mac_addr_len,
-                                 const uint8_t *bcast_addr, size_t bcast_addr_len,
-                                 uint16_t arp_type, uint16_t port) {
-        static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-        /* Default broadcast address for IPoIB */
-        static const uint8_t ib_bcast[] = {
-                0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
-                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                0xff, 0xff, 0xff, 0xff
-        };
-        struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } };
-        const uint8_t *default_bcast_addr;
-        size_t expected_bcast_addr_len;
-        uint8_t dhcp_hlen = 0;
-
-        if (arp_type == ARPHRD_ETHER) {
-                assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
-                memcpy(&eth_mac, mac_addr, ETH_ALEN);
-                dhcp_hlen = ETH_ALEN;
-
-                default_bcast_addr = eth_bcast;
-                expected_bcast_addr_len = ETH_ALEN;
-        } else if (arp_type == ARPHRD_INFINIBAND) {
-                default_bcast_addr = ib_bcast;
-                expected_bcast_addr_len = INFINIBAND_ALEN;
-        } else
-                return -EINVAL;
-
-        if (bcast_addr && bcast_addr_len > 0)
-                assert_return(bcast_addr_len == expected_bcast_addr_len, -EINVAL);
-        else {
-                bcast_addr = default_bcast_addr;
-                bcast_addr_len = expected_bcast_addr_len;
-        }
-
-        return _bind_raw_socket(ifindex, link, xid, bcast_addr, bcast_addr_len,
-                                &eth_mac, arp_type, dhcp_hlen, port);
-}
-
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
-        union sockaddr_union src = {
-                .in.sin_family = AF_INET,
-                .in.sin_port = htobe16(port),
-                .in.sin_addr.s_addr = address,
-        };
-        _cleanup_close_ int s = -1;
-        int r;
-
-        s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
-        if (s < 0)
-                return -errno;
-
-        if (ip_service_type >= 0)
-                r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type);
-        else
-                r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
-
-        if (r < 0)
-                return r;
-
-        r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
-        if (r < 0)
-                return r;
-
-        if (ifindex > 0) {
-                r = socket_bind_to_ifindex(s, ifindex);
-                if (r < 0)
-                        return r;
-        }
-
-        if (address == INADDR_ANY) {
-                r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
-                if (r < 0)
-                        return r;
-
-                r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true);
-                if (r < 0)
-                        return r;
-
-        } else {
-                r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true);
-                if (r < 0)
-                        return r;
-        }
-
-        r = bind(s, &src.sa, sizeof(src.in));
-        if (r < 0)
-                return -errno;
-
-        return TAKE_FD(s);
-}
-
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
-                                 const void *packet, size_t len) {
-        int r;
-
-        assert(link);
-        assert(packet);
-        assert(len);
-
-        r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll));
-        if (r < 0)
-                return -errno;
-
-        return 0;
-}
-
-int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
-                                 const void *packet, size_t len) {
-        union sockaddr_union dest = {
-                .in.sin_family = AF_INET,
-                .in.sin_port = htobe16(port),
-                .in.sin_addr.s_addr = address,
-        };
-        int r;
-
-        assert(s >= 0);
-        assert(packet);
-        assert(len);
-
-        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in));
-        if (r < 0)
-                return -errno;
-
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-option.c b/src/core/systemd/src/libsystemd-network/dhcp-option.c
deleted file mode 100644
index 2c15d85a..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-option.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include "alloc-util.h"
-#include "dhcp-internal.h"
-#include "dhcp-server-internal.h"
-#include "memory-util.h"
-#include "strv.h"
-#include "utf8.h"
-
-static int option_append(uint8_t options[], size_t size, size_t *offset,
-                         uint8_t code, size_t optlen, const void *optval) {
-        assert(options);
-        assert(offset);
-
-        if (code != SD_DHCP_OPTION_END)
-                /* always make sure there is space for an END option */
-                size--;
-
-        switch (code) {
-
-        case SD_DHCP_OPTION_PAD:
-        case SD_DHCP_OPTION_END:
-                if (*offset + 1 > size)
-                        return -ENOBUFS;
-
-                options[*offset] = code;
-                *offset += 1;
-                break;
-
-        case SD_DHCP_OPTION_USER_CLASS: {
-                size_t total = 0;
-                char **s;
-
-                STRV_FOREACH(s, (char **) optval) {
-                        size_t len = strlen(*s);
-
-                        if (len > 255)
-                                return -ENAMETOOLONG;
-
-                        total += 1 + len;
-                }
-
-                if (*offset + 2 + total > size)
-                        return -ENOBUFS;
-
-                options[*offset] = code;
-                options[*offset + 1] =  total;
-                *offset += 2;
-
-                STRV_FOREACH(s, (char **) optval) {
-                        size_t len = strlen(*s);
-
-                        options[*offset] = len;
-
-                        memcpy(&options[*offset + 1], *s, len);
-                        *offset += 1 + len;
-                }
-
-                break;
-        }
-        case SD_DHCP_OPTION_SIP_SERVER:
-                if (*offset + 3 + optlen > size)
-                        return -ENOBUFS;
-
-                options[*offset] = code;
-                options[*offset + 1] = optlen + 1;
-                options[*offset + 2] = 1;
-
-                memcpy_safe(&options[*offset + 3], optval, optlen);
-                *offset += 3 + optlen;
-
-                break;
-        case SD_DHCP_OPTION_VENDOR_SPECIFIC: {
-                OrderedHashmap *s = (OrderedHashmap *) optval;
-                struct sd_dhcp_option *p;
-                size_t l = 0;
-
-                ORDERED_HASHMAP_FOREACH(p, s)
-                        l += p->length + 2;
-
-                if (*offset + l + 2 > size)
-                        return -ENOBUFS;
-
-                options[*offset] = code;
-                options[*offset + 1] = l;
-
-                *offset += 2;
-
-                ORDERED_HASHMAP_FOREACH(p, s) {
-                        options[*offset] = p->option;
-                        options[*offset + 1] = p->length;
-                        memcpy(&options[*offset + 2], p->data, p->length);
-                        *offset += 2 + p->length;
-                }
-
-                break;
-        }
-        default:
-                if (*offset + 2 + optlen > size)
-                        return -ENOBUFS;
-
-                options[*offset] = code;
-                options[*offset + 1] = optlen;
-
-                memcpy_safe(&options[*offset + 2], optval, optlen);
-                *offset += 2 + optlen;
-
-                break;
-        }
-
-        return 0;
-}
-
-int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
-                       uint8_t overload,
-                       uint8_t code, size_t optlen, const void *optval) {
-        const bool use_file = overload & DHCP_OVERLOAD_FILE;
-        const bool use_sname = overload & DHCP_OVERLOAD_SNAME;
-        int r;
-
-        assert(message);
-        assert(offset);
-
-        /* If *offset is in range [0, size), we are writing to ->options,
-         * if *offset is in range [size, size + sizeof(message->file)) and use_file, we are writing to ->file,
-         * if *offset is in range [size + use_file*sizeof(message->file), size + use_file*sizeof(message->file) + sizeof(message->sname))
-         * and use_sname, we are writing to ->sname.
-         */
-
-        if (*offset < size) {
-                /* still space in the options array */
-                r = option_append(message->options, size, offset, code, optlen, optval);
-                if (r >= 0)
-                        return 0;
-                else if (r == -ENOBUFS && (use_file || use_sname)) {
-                        /* did not fit, but we have more buffers to try
-                           close the options array and move the offset to its end */
-                        r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
-                        if (r < 0)
-                                return r;
-
-                        *offset = size;
-                } else
-                        return r;
-        }
-
-        if (use_file) {
-                size_t file_offset = *offset - size;
-
-                if (file_offset < sizeof(message->file)) {
-                        /* still space in the 'file' array */
-                        r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval);
-                        if (r >= 0) {
-                                *offset = size + file_offset;
-                                return 0;
-                        } else if (r == -ENOBUFS && use_sname) {
-                                /* did not fit, but we have more buffers to try
-                                   close the file array and move the offset to its end */
-                                r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
-                                if (r < 0)
-                                        return r;
-
-                                *offset = size + sizeof(message->file);
-                        } else
-                                return r;
-                }
-        }
-
-        if (use_sname) {
-                size_t sname_offset = *offset - size - use_file*sizeof(message->file);
-
-                if (sname_offset < sizeof(message->sname)) {
-                        /* still space in the 'sname' array */
-                        r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval);
-                        if (r >= 0) {
-                                *offset = size + use_file*sizeof(message->file) + sname_offset;
-                                return 0;
-                        } else
-                                /* no space, or other error, give up */
-                                return r;
-                }
-        }
-
-        return -ENOBUFS;
-}
-
-static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
-                         uint8_t *message_type, char **error_message, dhcp_option_callback_t cb,
-                         void *userdata) {
-        uint8_t code, len;
-        const uint8_t *option;
-        size_t offset = 0;
-
-        while (offset < buflen) {
-                code = options[offset ++];
-
-                switch (code) {
-                case SD_DHCP_OPTION_PAD:
-                        continue;
-
-                case SD_DHCP_OPTION_END:
-                        return 0;
-                }
-
-                if (buflen < offset + 1)
-                        return -ENOBUFS;
-
-                len = options[offset ++];
-
-                if (buflen < offset + len)
-                        return -EINVAL;
-
-                option = &options[offset];
-
-                switch (code) {
-                case SD_DHCP_OPTION_MESSAGE_TYPE:
-                        if (len != 1)
-                                return -EINVAL;
-
-                        if (message_type)
-                                *message_type = *option;
-
-                        break;
-
-                case SD_DHCP_OPTION_ERROR_MESSAGE:
-                        if (len == 0)
-                                return -EINVAL;
-
-                        if (error_message) {
-                                _cleanup_free_ char *string = NULL;
-
-                                /* Accept a trailing NUL byte */
-                                if (memchr(option, 0, len - 1))
-                                        return -EINVAL;
-
-                                string = memdup_suffix0((const char *) option, len);
-                                if (!string)
-                                        return -ENOMEM;
-
-                                if (!ascii_is_valid(string))
-                                        return -EINVAL;
-
-                                free_and_replace(*error_message, string);
-                        }
-
-                        break;
-                case SD_DHCP_OPTION_OVERLOAD:
-                        if (len != 1)
-                                return -EINVAL;
-
-                        if (overload)
-                                *overload = *option;
-
-                        break;
-
-                default:
-                        if (cb)
-                                cb(code, len, option, userdata);
-
-                        break;
-                }
-
-                offset += len;
-        }
-
-        if (offset < buflen)
-                return -EINVAL;
-
-        return 0;
-}
-
-int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) {
-        _cleanup_free_ char *error_message = NULL;
-        uint8_t overload = 0;
-        uint8_t message_type = 0;
-        int r;
-
-        if (!message)
-                return -EINVAL;
-
-        if (len < sizeof(DHCPMessage))
-                return -EINVAL;
-
-        len -= sizeof(DHCPMessage);
-
-        r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
-        if (r < 0)
-                return r;
-
-        if (overload & DHCP_OVERLOAD_FILE) {
-                r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
-                if (r < 0)
-                        return r;
-        }
-
-        if (overload & DHCP_OVERLOAD_SNAME) {
-                r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
-                if (r < 0)
-                        return r;
-        }
-
-        if (message_type == 0)
-                return -ENOMSG;
-
-        if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE))
-                *_error_message = TAKE_PTR(error_message);
-
-        return message_type;
-}
-
-static sd_dhcp_option* dhcp_option_free(sd_dhcp_option *i) {
-        if (!i)
-                return NULL;
-
-        free(i->data);
-        return mfree(i);
-}
-
-int sd_dhcp_option_new(uint8_t option, const void *data, size_t length, sd_dhcp_option **ret) {
-        assert_return(ret, -EINVAL);
-        assert_return(length == 0 || data, -EINVAL);
-
-        _cleanup_free_ void *q = memdup(data, length);
-        if (!q)
-                return -ENOMEM;
-
-        sd_dhcp_option *p = new(sd_dhcp_option, 1);
-        if (!p)
-                return -ENOMEM;
-
-        *p = (sd_dhcp_option) {
-                .n_ref = 1,
-                .option = option,
-                .length = length,
-                .data = TAKE_PTR(q),
-        };
-
-        *ret = TAKE_PTR(p);
-        return 0;
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_option, sd_dhcp_option, dhcp_option_free);
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
-                dhcp_option_hash_ops,
-                void,
-                trivial_hash_func,
-                trivial_compare_func,
-                sd_dhcp_option,
-                sd_dhcp_option_unref);
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-packet.c b/src/core/systemd/src/libsystemd-network/dhcp-packet.c
deleted file mode 100644
index eeadd562..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-packet.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-#include <string.h>
-
-#include "dhcp-internal.h"
-#include "dhcp-protocol.h"
-
-#define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
-
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
-                      uint8_t type, uint16_t arp_type, size_t optlen,
-                      size_t *optoffset) {
-        size_t offset = 0;
-        int r;
-
-        assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
-        assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
-
-        message->op = op;
-        message->htype = arp_type;
-        message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
-        message->xid = htobe32(xid);
-        message->magic = htobe32(DHCP_MAGIC_COOKIE);
-
-        r = dhcp_option_append(message, optlen, &offset, 0,
-                               SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
-        if (r < 0)
-                return r;
-
-        *optoffset = offset;
-
-        return 0;
-}
-
-uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
-        uint64_t *buf_64 = (uint64_t*)buf;
-        uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t));
-        uint64_t sum = 0;
-
-        /* See RFC1071 */
-
-        while (buf_64 < end_64) {
-                sum += *buf_64;
-                if (sum < *buf_64)
-                        /* wrap around in one's complement */
-                        sum++;
-
-                buf_64++;
-        }
-
-        if (len % sizeof(uint64_t)) {
-                /* If the buffer is not aligned to 64-bit, we need
-                   to zero-pad the last few bytes and add them in */
-                uint64_t buf_tail = 0;
-
-                memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
-
-                sum += buf_tail;
-                if (sum < buf_tail)
-                        /* wrap around */
-                        sum++;
-        }
-
-        while (sum >> 16)
-                sum = (sum & 0xffff) + (sum >> 16);
-
-        return ~sum;
-}
-
-void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
-                                   uint16_t source_port, be32_t destination_addr,
-                                   uint16_t destination_port, uint16_t len, int ip_service_type) {
-        packet->ip.version = IPVERSION;
-        packet->ip.ihl = DHCP_IP_SIZE / 4;
-        packet->ip.tot_len = htobe16(len);
-
-        if (ip_service_type >= 0)
-                packet->ip.tos = ip_service_type;
-        else
-                packet->ip.tos = IPTOS_CLASS_CS6;
-
-        packet->ip.protocol = IPPROTO_UDP;
-        packet->ip.saddr = source_addr;
-        packet->ip.daddr = destination_addr;
-
-        packet->udp.source = htobe16(source_port);
-        packet->udp.dest = htobe16(destination_port);
-
-        packet->udp.len = htobe16(len - DHCP_IP_SIZE);
-
-        packet->ip.check = packet->udp.len;
-        packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
-
-        packet->ip.ttl = IPDEFTTL;
-        packet->ip.check = 0;
-        packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
-}
-
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) {
-        size_t hdrlen;
-
-        assert(packet);
-
-        /* IP */
-
-        if (packet->ip.version != IPVERSION)
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: not IPv4");
-
-        if (packet->ip.ihl < 5)
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: IPv4 IHL (%u words) invalid",
-                                       packet->ip.ihl);
-
-        hdrlen = packet->ip.ihl * 4;
-        if (hdrlen < 20)
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: IPv4 IHL (%zu bytes) "
-                                       "smaller than minimum (20 bytes)",
-                                       hdrlen);
-
-        if (len < hdrlen)
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: packet (%zu bytes) "
-                                       "smaller than expected (%zu) by IP header",
-                                       len, hdrlen);
-
-        /* UDP */
-
-        if (packet->ip.protocol != IPPROTO_UDP)
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: not UDP");
-
-        if (len < hdrlen + be16toh(packet->udp.len))
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: packet (%zu bytes) "
-                                       "smaller than expected (%zu) by UDP header",
-                                       len, hdrlen + be16toh(packet->udp.len));
-
-        if (be16toh(packet->udp.dest) != port)
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: to port %u, which "
-                                       "is not the DHCP client port (%u)",
-                                       be16toh(packet->udp.dest), port);
-
-        /* checksums - computing these is relatively expensive, so only do it
-           if all the other checks have passed
-         */
-
-        if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen))
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "ignoring packet: invalid IP checksum");
-
-        if (checksum && packet->udp.check) {
-                packet->ip.check = packet->udp.len;
-                packet->ip.ttl = 0;
-
-                if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
-                                  be16toh(packet->udp.len) + 12))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "ignoring packet: invalid UDP checksum");
-        }
-
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-protocol.h b/src/core/systemd/src/libsystemd-network/dhcp-protocol.h
deleted file mode 100644
index 11f4201a..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp-protocol.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-#include <stdint.h>
-
-#include "macro.h"
-#include "sparse-endian.h"
-
-struct DHCPMessage {
-        uint8_t op;
-        uint8_t htype;
-        uint8_t hlen;
-        uint8_t hops;
-        be32_t xid;
-        be16_t secs;
-        be16_t flags;
-        be32_t ciaddr;
-        be32_t yiaddr;
-        be32_t siaddr;
-        be32_t giaddr;
-        uint8_t chaddr[16];
-        uint8_t sname[64];
-        uint8_t file[128];
-        be32_t magic;
-        uint8_t options[0];
-} _packed_;
-
-typedef struct DHCPMessage DHCPMessage;
-
-struct DHCPPacket {
-        struct iphdr ip;
-        struct udphdr udp;
-        DHCPMessage dhcp;
-} _packed_;
-
-typedef struct DHCPPacket DHCPPacket;
-
-#define DHCP_IP_SIZE            (int32_t)(sizeof(struct iphdr))
-#define DHCP_IP_UDP_SIZE        (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
-#define DHCP_MESSAGE_SIZE       (int32_t)(sizeof(DHCPMessage))
-#define DHCP_DEFAULT_MIN_SIZE   576 /* the minimum internet hosts must be able to receive */
-#define DHCP_MIN_OPTIONS_SIZE   (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE)
-#define DHCP_MAGIC_COOKIE       (uint32_t)(0x63825363)
-
-enum {
-        DHCP_PORT_SERVER                        = 67,
-        DHCP_PORT_CLIENT                        = 68,
-};
-
-enum DHCPState {
-        DHCP_STATE_INIT                         = 0,
-        DHCP_STATE_SELECTING                    = 1,
-        DHCP_STATE_INIT_REBOOT                  = 2,
-        DHCP_STATE_REBOOTING                    = 3,
-        DHCP_STATE_REQUESTING                   = 4,
-        DHCP_STATE_BOUND                        = 5,
-        DHCP_STATE_RENEWING                     = 6,
-        DHCP_STATE_REBINDING                    = 7,
-        DHCP_STATE_STOPPED                      = 8,
-};
-
-typedef enum DHCPState DHCPState;
-
-enum {
-        BOOTREQUEST                             = 1,
-        BOOTREPLY                               = 2,
-};
-
-enum {
-        DHCP_DISCOVER                           = 1,
-        DHCP_OFFER                              = 2,
-        DHCP_REQUEST                            = 3,
-        DHCP_DECLINE                            = 4,
-        DHCP_ACK                                = 5,
-        DHCP_NAK                                = 6,
-        DHCP_RELEASE                            = 7,
-        DHCP_INFORM                             = 8,
-        DHCP_FORCERENEW                         = 9,
-};
-
-enum {
-        DHCP_OVERLOAD_FILE                      = 1,
-        DHCP_OVERLOAD_SNAME                     = 2,
-};
-
-#define DHCP_MAX_FQDN_LENGTH 255
-
-enum {
-        DHCP_FQDN_FLAG_S = (1 << 0),
-        DHCP_FQDN_FLAG_O = (1 << 1),
-        DHCP_FQDN_FLAG_E = (1 << 2),
-        DHCP_FQDN_FLAG_N = (1 << 3),
-};
diff --git a/src/core/systemd/src/libsystemd-network/dhcp6-internal.h b/src/core/systemd/src/libsystemd-network/dhcp6-internal.h
deleted file mode 100644
index 24d8a314..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp6-internal.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2014-2015 Intel Corporation. All rights reserved.
-***/
-
-#include <net/ethernet.h>
-#include <netinet/in.h>
-
-#include "sd-event.h"
-
-#include "list.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "sparse-endian.h"
-
-typedef struct sd_dhcp6_option {
-        unsigned n_ref;
-
-        uint32_t enterprise_identifier;
-        uint16_t option;
-        void *data;
-        size_t length;
-} sd_dhcp6_option;
-
-extern const struct hash_ops dhcp6_option_hash_ops;
-
-/* Common option header */
-typedef struct DHCP6Option {
-        be16_t code;
-        be16_t len;
-        uint8_t data[];
-} _packed_ DHCP6Option;
-
-/* Address option */
-struct iaaddr {
-        struct in6_addr address;
-        be32_t lifetime_preferred;
-        be32_t lifetime_valid;
-} _packed_;
-
-/* Prefix Delegation Prefix option */
-struct iapdprefix {
-        be32_t lifetime_preferred;
-        be32_t lifetime_valid;
-        uint8_t prefixlen;
-        struct in6_addr address;
-} _packed_;
-
-typedef struct DHCP6Address DHCP6Address;
-
-struct DHCP6Address {
-        LIST_FIELDS(DHCP6Address, addresses);
-
-        union {
-                struct iaaddr iaaddr;
-                struct iapdprefix iapdprefix;
-        };
-};
-
-/* Non-temporary Address option */
-struct ia_na {
-        be32_t id;
-        be32_t lifetime_t1;
-        be32_t lifetime_t2;
-} _packed_;
-
-/* Prefix Delegation option */
-struct ia_pd {
-        be32_t id;
-        be32_t lifetime_t1;
-        be32_t lifetime_t2;
-} _packed_;
-
-/* Temporary Address option */
-struct ia_ta {
-        be32_t id;
-} _packed_;
-
-struct DHCP6IA {
-        uint16_t type;
-        union {
-                struct ia_na ia_na;
-                struct ia_pd ia_pd;
-                struct ia_ta ia_ta;
-        };
-
-        LIST_HEAD(DHCP6Address, addresses);
-};
-
-typedef struct DHCP6IA DHCP6IA;
-
-#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
-#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__)
-
-int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
-                        size_t optlen, const void *optval);
-int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
-int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
-int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
-int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
-int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
-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, size_t len);
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
-int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
-                                struct in6_addr **addrs, size_t count,
-                                size_t *allocated);
-int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen,
-                                       char ***str_arr);
-int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str);
-
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
-                                  const void *packet, size_t len);
-
-const char *dhcp6_message_type_to_string(int s) _const_;
-int dhcp6_message_type_from_string(const char *s) _pure_;
-const char *dhcp6_message_status_to_string(int s) _const_;
-int dhcp6_message_status_from_string(const char *s) _pure_;
diff --git a/src/core/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/core/systemd/src/libsystemd-network/dhcp6-lease-internal.h
deleted file mode 100644
index e9e2362d..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp6-lease-internal.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2014-2015 Intel Corporation. All rights reserved.
-***/
-
-#include <stdint.h>
-
-#include "sd-dhcp6-lease.h"
-
-#include "dhcp6-internal.h"
-
-struct sd_dhcp6_lease {
-        unsigned n_ref;
-
-        uint8_t *serverid;
-        size_t serverid_len;
-        uint8_t preference;
-        bool rapid_commit;
-
-        DHCP6IA ia;
-        DHCP6IA pd;
-
-        DHCP6Address *addr_iter;
-        DHCP6Address *prefix_iter;
-
-        struct in6_addr *dns;
-        size_t dns_count;
-        size_t dns_allocated;
-        char **domains;
-        size_t domains_count;
-        struct in6_addr *ntp;
-        size_t ntp_count;
-        size_t ntp_allocated;
-        char **ntp_fqdn;
-        size_t ntp_fqdn_count;
-        char *fqdn;
-};
-
-int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
-DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
-
-int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
-                             size_t len);
-int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len);
-int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
-int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
-int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
-int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
-
-int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
-int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
-
-int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
-int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
-                            size_t optlen);
-int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
-int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
-                         size_t optlen) ;
-int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
-
-int dhcp6_lease_new(sd_dhcp6_lease **ret);
diff --git a/src/core/systemd/src/libsystemd-network/dhcp6-network.c b/src/core/systemd/src/libsystemd-network/dhcp6-network.c
deleted file mode 100644
index b6816de4..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp6-network.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <netinet/in.h>
-#include <netinet/ip6.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <linux/if_packet.h>
-
-#include "dhcp6-internal.h"
-#include "dhcp6-protocol.h"
-#include "fd-util.h"
-#include "socket-util.h"
-
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
-        union sockaddr_union src = {
-                .in6.sin6_family = AF_INET6,
-                .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
-                .in6.sin6_scope_id = ifindex,
-        };
-        _cleanup_close_ int s = -1;
-        int r;
-
-        assert(ifindex > 0);
-        assert(local_address);
-
-        src.in6.sin6_addr = *local_address;
-
-        s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
-        if (s < 0)
-                return -errno;
-
-        r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
-        if (r < 0)
-                return r;
-
-        r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false);
-        if (r < 0)
-                return r;
-
-        r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
-        if (r < 0)
-                return r;
-
-        r = bind(s, &src.sa, sizeof(src.in6));
-        if (r < 0)
-                return -errno;
-
-        return TAKE_FD(s);
-}
-
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
-                                  const void *packet, size_t len) {
-        union sockaddr_union dest = {
-                .in6.sin6_family = AF_INET6,
-                .in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
-        };
-        int r;
-
-        assert(server_address);
-
-        memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
-
-        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
-        if (r < 0)
-                return -errno;
-
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/dhcp6-option.c b/src/core/systemd/src/libsystemd-network/dhcp6-option.c
deleted file mode 100644
index 21e2d3f0..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp6-option.c
+++ /dev/null
@@ -1,795 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014-2015 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <netinet/in.h>
-
-#include "sd-dhcp6-client.h"
-
-#include "alloc-util.h"
-#include "dhcp-identifier.h"
-#include "dhcp6-internal.h"
-#include "dhcp6-lease-internal.h"
-#include "dhcp6-protocol.h"
-#include "dns-domain.h"
-#include "memory-util.h"
-#include "sparse-endian.h"
-#include "strv.h"
-#include "unaligned.h"
-
-typedef struct DHCP6StatusOption {
-        struct DHCP6Option option;
-        be16_t status;
-        char msg[];
-} _packed_ DHCP6StatusOption;
-
-typedef struct DHCP6AddressOption {
-        struct DHCP6Option option;
-        struct iaaddr iaaddr;
-        uint8_t options[];
-} _packed_ DHCP6AddressOption;
-
-typedef struct DHCP6PDPrefixOption {
-        struct DHCP6Option option;
-        struct iapdprefix iapdprefix;
-        uint8_t options[];
-} _packed_ DHCP6PDPrefixOption;
-
-#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
-#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
-#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
-
-static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
-                             size_t optlen) {
-        DHCP6Option *option = (DHCP6Option*) *buf;
-
-        assert_return(buf, -EINVAL);
-        assert_return(*buf, -EINVAL);
-        assert_return(buflen, -EINVAL);
-
-        if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
-                return -ENOBUFS;
-
-        option->code = htobe16(optcode);
-        option->len = htobe16(optlen);
-
-        *buf += offsetof(DHCP6Option, data);
-        *buflen -= offsetof(DHCP6Option, data);
-
-        return 0;
-}
-
-int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
-                        size_t optlen, const void *optval) {
-        int r;
-
-        assert_return(optval || optlen == 0, -EINVAL);
-
-        r = option_append_hdr(buf, buflen, code, optlen);
-        if (r < 0)
-                return r;
-
-        memcpy_safe(*buf, optval, optlen);
-
-        *buf += optlen;
-        *buflen -= optlen;
-
-        return 0;
-}
-
-int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
-        sd_dhcp6_option *options;
-        int r;
-
-        assert(buf);
-        assert(*buf);
-        assert(buflen);
-        assert(vendor_options);
-
-        ORDERED_HASHMAP_FOREACH(options, vendor_options) {
-                _cleanup_free_ uint8_t *p = NULL;
-                size_t total;
-
-                total = 4 + 2 + 2 + options->length;
-
-                p = malloc(total);
-                if (!p)
-                        return -ENOMEM;
-
-                unaligned_write_be32(p, options->enterprise_identifier);
-                unaligned_write_be16(p + 4, options->option);
-                unaligned_write_be16(p + 6, options->length);
-                memcpy(p + 8, options->data, options->length);
-
-                r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
-        uint16_t len;
-        uint8_t *ia_hdr;
-        size_t iaid_offset, ia_buflen, ia_addrlen = 0;
-        DHCP6Address *addr;
-        int r;
-
-        assert_return(buf, -EINVAL);
-        assert_return(*buf, -EINVAL);
-        assert_return(buflen, -EINVAL);
-        assert_return(ia, -EINVAL);
-
-        switch (ia->type) {
-        case SD_DHCP6_OPTION_IA_NA:
-                len = DHCP6_OPTION_IA_NA_LEN;
-                iaid_offset = offsetof(DHCP6IA, ia_na);
-                break;
-
-        case SD_DHCP6_OPTION_IA_TA:
-                len = DHCP6_OPTION_IA_TA_LEN;
-                iaid_offset = offsetof(DHCP6IA, ia_ta);
-                break;
-
-        default:
-                return -EINVAL;
-        }
-
-        if (*buflen < offsetof(DHCP6Option, data) + len)
-                return -ENOBUFS;
-
-        ia_hdr = *buf;
-        ia_buflen = *buflen;
-
-        *buf += offsetof(DHCP6Option, data);
-        *buflen -= offsetof(DHCP6Option, data);
-
-        memcpy(*buf, (char*) ia + iaid_offset, len);
-
-        *buf += len;
-        *buflen -= len;
-
-        LIST_FOREACH(addresses, addr, ia->addresses) {
-                r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
-                                      sizeof(addr->iaaddr));
-                if (r < 0)
-                        return r;
-
-                memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
-
-                *buf += sizeof(addr->iaaddr);
-                *buflen -= sizeof(addr->iaaddr);
-
-                ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr);
-        }
-
-        r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
-int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
-        uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX];
-        int r;
-
-        assert_return(buf && *buf && buflen && fqdn, -EINVAL);
-
-        buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */
-
-        /* Store domain name after flags field */
-        r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1,  false);
-        if (r <= 0)
-                return r;
-
-        /*
-         * According to RFC 4704, chapter 4.2 only add terminating zero-length
-         * label in case a FQDN is provided. Since dns_name_to_wire_format
-         * always adds terminating zero-length label remove if only a hostname
-         * is provided.
-         */
-        if (dns_name_is_single_label(fqdn))
-                r--;
-
-        r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
-
-        return r;
-}
-
-int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) {
-        _cleanup_free_ uint8_t *p = NULL;
-        size_t total = 0, offset = 0;
-        char **s;
-
-        assert_return(buf && *buf && buflen && user_class, -EINVAL);
-
-        STRV_FOREACH(s, user_class) {
-                size_t len = strlen(*s);
-                uint8_t *q;
-
-                if (len > 0xffff)
-                        return -ENAMETOOLONG;
-                q = realloc(p, total + len + 2);
-                if (!q)
-                        return -ENOMEM;
-
-                p = q;
-
-                unaligned_write_be16(&p[offset], len);
-                memcpy(&p[offset + 2], *s, len);
-
-                offset += 2 + len;
-                total += 2 + len;
-        }
-
-        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p);
-}
-
-int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) {
-        _cleanup_free_ uint8_t *p = NULL;
-        uint32_t enterprise_identifier;
-        size_t total, offset;
-        char **s;
-
-        assert(buf);
-        assert(*buf);
-        assert(buflen);
-        assert(vendor_class);
-
-        enterprise_identifier = htobe32(SYSTEMD_PEN);
-
-        p = memdup(&enterprise_identifier, sizeof(enterprise_identifier));
-        if (!p)
-                return -ENOMEM;
-
-        total = sizeof(enterprise_identifier);
-        offset = total;
-
-        STRV_FOREACH(s, vendor_class) {
-                size_t len = strlen(*s);
-                uint8_t *q;
-
-                q = realloc(p, total + len + 2);
-                if (!q)
-                        return -ENOMEM;
-
-                p = q;
-
-                unaligned_write_be16(&p[offset], len);
-                memcpy(&p[offset + 2], *s, len);
-
-                offset += 2 + len;
-                total += 2 + len;
-        }
-
-        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
-}
-
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
-        DHCP6Option *option = (DHCP6Option *)buf;
-        size_t i = sizeof(*option) + sizeof(pd->ia_pd);
-        DHCP6PDPrefixOption *prefix_opt;
-        DHCP6Address *prefix;
-
-        assert_return(buf, -EINVAL);
-        assert_return(pd, -EINVAL);
-        assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
-
-        if (len < i)
-                return -ENOBUFS;
-
-        option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
-
-        memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
-        LIST_FOREACH(addresses, prefix, pd->addresses) {
-                if (len < i + sizeof(*prefix_opt))
-                        return -ENOBUFS;
-
-                prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
-                prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
-                prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
-
-                memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, sizeof(struct iapdprefix));
-                i += sizeof(*prefix_opt);
-        }
-
-        if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
-                if (len < i + sizeof(*prefix_opt))
-                        return -ENOBUFS;
-
-                prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
-                prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
-                prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
-
-                memcpy(&prefix_opt->iapdprefix, &hint_pd_prefix->iapdprefix, sizeof(struct iapdprefix));
-                i += sizeof(*prefix_opt);
-        }
-
-        option->len = htobe16(i - sizeof(*option));
-
-        return i;
-}
-
-static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
-        DHCP6Option *option = (DHCP6Option*) *buf;
-        uint16_t len;
-
-        assert_return(buf, -EINVAL);
-        assert_return(optcode, -EINVAL);
-        assert_return(optlen, -EINVAL);
-
-        if (*buflen < offsetof(DHCP6Option, data))
-                return -ENOMSG;
-
-        len = be16toh(option->len);
-
-        if (len > *buflen)
-                return -ENOMSG;
-
-        *optcode = be16toh(option->code);
-        *optlen = len;
-
-        *buf += 4;
-        *buflen -= 4;
-
-        return 0;
-}
-
-int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
-                       size_t *optlen, uint8_t **optvalue) {
-        int r;
-
-        assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
-
-        r = option_parse_hdr(buf, buflen, optcode, optlen);
-        if (r < 0)
-                return r;
-
-        if (*optlen > *buflen)
-                return -ENOBUFS;
-
-        *optvalue = *buf;
-        *buflen -= *optlen;
-        *buf += *optlen;
-
-        return 0;
-}
-
-int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
-        DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
-
-        if (len < sizeof(DHCP6StatusOption) ||
-            be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
-                return -ENOBUFS;
-
-        return be16toh(statusopt->status);
-}
-
-static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
-                                      uint32_t *lifetime_valid) {
-        DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
-        DHCP6Address *addr;
-        uint32_t lt_valid, lt_pref;
-        int r;
-
-        if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
-                return -ENOBUFS;
-
-        lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
-        lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
-
-        if (lt_valid == 0 || lt_pref > lt_valid) {
-                log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
-                                 lt_pref, lt_valid);
-
-                return 0;
-        }
-
-        if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
-                r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
-                if (r != 0)
-                        return r < 0 ? r: 0;
-        }
-
-        addr = new0(DHCP6Address, 1);
-        if (!addr)
-                return -ENOMEM;
-
-        LIST_INIT(addresses, addr);
-        memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
-
-        LIST_PREPEND(addresses, ia->addresses, addr);
-
-        *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
-
-        return 0;
-}
-
-static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
-                                       uint32_t *lifetime_valid) {
-        DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
-        DHCP6Address *prefix;
-        uint32_t lt_valid, lt_pref;
-        int r;
-
-        if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
-                return -ENOBUFS;
-
-        lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
-        lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
-
-        if (lt_valid == 0 || lt_pref > lt_valid) {
-                log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
-                                 lt_pref, lt_valid);
-
-                return 0;
-        }
-
-        if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
-                r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
-                if (r != 0)
-                        return r < 0 ? r: 0;
-        }
-
-        prefix = new0(DHCP6Address, 1);
-        if (!prefix)
-                return -ENOMEM;
-
-        LIST_INIT(addresses, prefix);
-        memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
-
-        LIST_PREPEND(addresses, ia->addresses, prefix);
-
-        *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
-
-        return 0;
-}
-
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
-        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
-        uint16_t iatype, optlen;
-        size_t iaaddr_offset;
-        int r = 0, status;
-        size_t i, len;
-        uint16_t opt;
-
-        assert_return(ia, -EINVAL);
-        assert_return(!ia->addresses, -EINVAL);
-
-        iatype = be16toh(iaoption->code);
-        len = be16toh(iaoption->len);
-
-        switch (iatype) {
-        case SD_DHCP6_OPTION_IA_NA:
-
-                if (len < DHCP6_OPTION_IA_NA_LEN)
-                        return -ENOBUFS;
-
-                iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
-                memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
-
-                lt_t1 = be32toh(ia->ia_na.lifetime_t1);
-                lt_t2 = be32toh(ia->ia_na.lifetime_t2);
-
-                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
-                        log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
-                                         lt_t1, lt_t2);
-                        return -EINVAL;
-                }
-
-                break;
-
-        case SD_DHCP6_OPTION_IA_PD:
-
-                if (len < sizeof(ia->ia_pd))
-                        return -ENOBUFS;
-
-                iaaddr_offset = sizeof(ia->ia_pd);
-                memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
-
-                lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
-                lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
-
-                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
-                        log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
-                                         lt_t1, lt_t2);
-                        return -EINVAL;
-                }
-
-                break;
-
-        case SD_DHCP6_OPTION_IA_TA:
-                if (len < DHCP6_OPTION_IA_TA_LEN)
-                        return -ENOBUFS;
-
-                iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
-                memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
-
-                break;
-
-        default:
-                return -ENOMSG;
-        }
-
-        ia->type = iatype;
-        i = iaaddr_offset;
-
-        while (i < len) {
-                DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
-
-                if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
-                        return -ENOBUFS;
-
-                opt = be16toh(option->code);
-                optlen = be16toh(option->len);
-
-                switch (opt) {
-                case SD_DHCP6_OPTION_IAADDR:
-
-                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
-                                log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
-                                return -EINVAL;
-                        }
-
-                        r = dhcp6_option_parse_address(option, ia, &lt_valid);
-                        if (r < 0)
-                                return r;
-
-                        if (lt_valid < lt_min)
-                                lt_min = lt_valid;
-
-                        break;
-
-                case SD_DHCP6_OPTION_IA_PD_PREFIX:
-
-                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
-                                log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
-                                return -EINVAL;
-                        }
-
-                        r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
-                        if (r < 0)
-                                return r;
-
-                        if (lt_valid < lt_min)
-                                lt_min = lt_valid;
-
-                        break;
-
-                case SD_DHCP6_OPTION_STATUS_CODE:
-
-                        status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
-                        if (status < 0)
-                                return status;
-
-                        if (status > 0) {
-                                if (ret_status_code)
-                                        *ret_status_code = status;
-
-                                log_dhcp6_client(client, "IA status %s",
-                                                 dhcp6_message_status_to_string(status));
-
-                                return 0;
-                        }
-
-                        break;
-
-                default:
-                        log_dhcp6_client(client, "Unknown IA option %d", opt);
-                        break;
-                }
-
-                i += sizeof(*option) + optlen;
-        }
-
-        switch(iatype) {
-        case SD_DHCP6_OPTION_IA_NA:
-                if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
-                        lt_t1 = lt_min / 2;
-                        lt_t2 = lt_min / 10 * 8;
-                        ia->ia_na.lifetime_t1 = htobe32(lt_t1);
-                        ia->ia_na.lifetime_t2 = htobe32(lt_t2);
-
-                        log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
-                                         lt_t1, lt_t2);
-                }
-
-                break;
-
-        case SD_DHCP6_OPTION_IA_PD:
-                if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
-                        lt_t1 = lt_min / 2;
-                        lt_t2 = lt_min / 10 * 8;
-                        ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
-                        ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
-
-                        log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
-                                         lt_t1, lt_t2);
-                }
-
-                break;
-
-        default:
-                break;
-        }
-
-        if (ret_status_code)
-                *ret_status_code = 0;
-
-        return 1;
-}
-
-int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
-                                struct in6_addr **addrs, size_t count,
-                                size_t *allocated) {
-
-        if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
-                return -EINVAL;
-
-        if (!GREEDY_REALLOC(*addrs, *allocated,
-                            count * sizeof(struct in6_addr) + optlen))
-                return -ENOMEM;
-
-        memcpy(*addrs + count, optval, optlen);
-
-        count += optlen / sizeof(struct in6_addr);
-
-        return count;
-}
-
-static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) {
-        _cleanup_free_ char *ret = NULL;
-        size_t n = 0, allocated = 0;
-        const uint8_t *optval = *data;
-        uint16_t optlen = *len;
-        bool first = true;
-        int r;
-
-        if (optlen <= 1)
-                return -ENODATA;
-
-        for (;;) {
-                const char *label;
-                uint8_t c;
-
-                if (optlen == 0)
-                        break;
-
-                c = *optval;
-                optval++;
-                optlen--;
-
-                if (c == 0)
-                        /* End label */
-                        break;
-                if (c > 63)
-                        return -EBADMSG;
-                if (c > optlen)
-                        return -EMSGSIZE;
-
-                /* Literal label */
-                label = (const char *)optval;
-                optval += c;
-                optlen -= c;
-
-                if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
-                        return -ENOMEM;
-
-                if (first)
-                        first = false;
-                else
-                        ret[n++] = '.';
-
-                r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
-                if (r < 0)
-                        return r;
-
-                n += r;
-        }
-
-        if (n) {
-                if (!GREEDY_REALLOC(ret, allocated, n + 1))
-                        return -ENOMEM;
-                ret[n] = 0;
-        }
-
-        *out_domain = TAKE_PTR(ret);
-        *data = optval;
-        *len = optlen;
-
-        return n;
-}
-
-int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) {
-        _cleanup_free_ char *domain = NULL;
-        int r;
-
-        r = parse_domain(&optval, &optlen, &domain);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -ENODATA;
-        if (optlen != 0)
-                return -EINVAL;
-
-        *str = TAKE_PTR(domain);
-        return 0;
-}
-
-int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
-        size_t idx = 0;
-        _cleanup_strv_free_ char **names = NULL;
-        int r;
-
-        if (optlen <= 1)
-                return -ENODATA;
-        if (optval[optlen - 1] != '\0')
-                return -EINVAL;
-
-        while (optlen > 0) {
-                _cleanup_free_ char *ret = NULL;
-
-                r = parse_domain(&optval, &optlen, &ret);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        continue;
-
-                r = strv_extend(&names, ret);
-                if (r < 0)
-                        return r;
-
-                idx++;
-        }
-
-        *str_arr = TAKE_PTR(names);
-
-        return idx;
-}
-
-static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
-        if (!i)
-                return NULL;
-
-        free(i->data);
-        return mfree(i);
-}
-
-int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) {
-        assert_return(ret, -EINVAL);
-        assert_return(length == 0 || data, -EINVAL);
-
-        _cleanup_free_ void *q = memdup(data, length);
-        if (!q)
-                return -ENOMEM;
-
-        sd_dhcp6_option *p = new(sd_dhcp6_option, 1);
-        if (!p)
-                return -ENOMEM;
-
-        *p = (sd_dhcp6_option) {
-                .n_ref = 1,
-                .option = option,
-                .enterprise_identifier = enterprise_identifier,
-                .length = length,
-                .data = TAKE_PTR(q),
-        };
-
-        *ret = p;
-        return 0;
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option, sd_dhcp6_option, dhcp6_option_free);
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
-                dhcp6_option_hash_ops,
-                void,
-                trivial_hash_func,
-                trivial_compare_func,
-                sd_dhcp6_option,
-                sd_dhcp6_option_unref);
diff --git a/src/core/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/core/systemd/src/libsystemd-network/dhcp6-protocol.h
deleted file mode 100644
index c7003638..00000000
--- a/src/core/systemd/src/libsystemd-network/dhcp6-protocol.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2014 Intel Corporation. All rights reserved.
-***/
-
-#include <netinet/ip6.h>
-#include <netinet/udp.h>
-
-#include "macro.h"
-#include "sparse-endian.h"
-
-struct DHCP6Message {
-        union {
-                struct {
-                        uint8_t type;
-                        uint8_t _pad[3];
-                } _packed_;
-                be32_t transaction_id;
-        };
-        uint8_t options[];
-} _packed_;
-
-typedef struct DHCP6Message DHCP6Message;
-
-#define DHCP6_MIN_OPTIONS_SIZE \
-        1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
-
-#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
-        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
-              0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
-
-enum {
-        DHCP6_PORT_SERVER                       = 547,
-        DHCP6_PORT_CLIENT                       = 546,
-};
-
-#define DHCP6_INF_TIMEOUT                       1 * USEC_PER_SEC
-#define DHCP6_INF_MAX_RT                        120 * USEC_PER_SEC
-#define DHCP6_SOL_MAX_DELAY                     1 * USEC_PER_SEC
-#define DHCP6_SOL_TIMEOUT                       1 * USEC_PER_SEC
-#define DHCP6_SOL_MAX_RT                        120 * USEC_PER_SEC
-#define DHCP6_REQ_TIMEOUT                       1 * USEC_PER_SEC
-#define DHCP6_REQ_MAX_RT                        120 * USEC_PER_SEC
-#define DHCP6_REQ_MAX_RC                        10
-#define DHCP6_REN_TIMEOUT                       10 * USEC_PER_SEC
-#define DHCP6_REN_MAX_RT                        600 * USEC_PER_SEC
-#define DHCP6_REB_TIMEOUT                       10 * USEC_PER_SEC
-#define DHCP6_REB_MAX_RT                        600 * USEC_PER_SEC
-
-enum DHCP6State {
-        DHCP6_STATE_STOPPED                     = 0,
-        DHCP6_STATE_INFORMATION_REQUEST         = 1,
-        DHCP6_STATE_SOLICITATION                = 2,
-        DHCP6_STATE_REQUEST                     = 3,
-        DHCP6_STATE_BOUND                       = 4,
-        DHCP6_STATE_RENEW                       = 5,
-        DHCP6_STATE_REBIND                      = 6,
-};
-
-enum {
-        DHCP6_SOLICIT                           = 1,
-        DHCP6_ADVERTISE                         = 2,
-        DHCP6_REQUEST                           = 3,
-        DHCP6_CONFIRM                           = 4,
-        DHCP6_RENEW                             = 5,
-        DHCP6_REBIND                            = 6,
-        DHCP6_REPLY                             = 7,
-        DHCP6_RELEASE                           = 8,
-        DHCP6_DECLINE                           = 9,
-        DHCP6_RECONFIGURE                       = 10,
-        DHCP6_INFORMATION_REQUEST               = 11,
-        DHCP6_RELAY_FORW                        = 12,
-        DHCP6_RELAY_REPL                        = 13,
-        _DHCP6_MESSAGE_MAX                      = 14,
-};
-
-enum {
-        DHCP6_NTP_SUBOPTION_SRV_ADDR            = 1,
-        DHCP6_NTP_SUBOPTION_MC_ADDR             = 2,
-        DHCP6_NTP_SUBOPTION_SRV_FQDN            = 3,
-};
-
-/*
- * RFC 8415, RFC 5007 and RFC 7653 status codes:
- * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
- */
-enum {
-        DHCP6_STATUS_SUCCESS                      = 0,
-        DHCP6_STATUS_UNSPEC_FAIL                  = 1,
-        DHCP6_STATUS_NO_ADDRS_AVAIL               = 2,
-        DHCP6_STATUS_NO_BINDING                   = 3,
-        DHCP6_STATUS_NOT_ON_LINK                  = 4,
-        DHCP6_STATUS_USE_MULTICAST                = 5,
-        DHCP6_STATUS_NO_PREFIX_AVAIL              = 6,
-        DHCP6_STATUS_UNKNOWN_QUERY_TYPE           = 7,
-        DHCP6_STATUS_MALFORMED_QUERY              = 8,
-        DHCP6_STATUS_NOT_CONFIGURED               = 9,
-        DHCP6_STATUS_NOT_ALLOWED                  = 10,
-        DHCP6_STATUS_QUERY_TERMINATED             = 11,
-        DHCP6_STATUS_DATA_MISSING                 = 12,
-        DHCP6_STATUS_CATCHUP_COMPLETE             = 13,
-        DHCP6_STATUS_NOT_SUPPORTED                = 14,
-        DHCP6_STATUS_TLS_CONNECTION_REFUSED       = 15,
-        DHCP6_STATUS_ADDRESS_IN_USE               = 16,
-        DHCP6_STATUS_CONFIGURATION_CONFLICT       = 17,
-        DHCP6_STATUS_MISSING_BINDING_INFORMATION  = 18,
-        DHCP6_STATUS_OUTDATED_BINDING_INFORMATION = 19,
-        DHCP6_STATUS_SERVER_SHUTTING_DOWN         = 20,
-        DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED     = 21,
-        DHCP6_STATUS_EXCESSIVE_TIME_SKEW          = 22,
-        _DHCP6_STATUS_MAX                         = 23,
-};
-
-enum {
-        DHCP6_FQDN_FLAG_S = (1 << 0),
-        DHCP6_FQDN_FLAG_O = (1 << 1),
-        DHCP6_FQDN_FLAG_N = (1 << 2),
-};
diff --git a/src/core/systemd/src/libsystemd-network/lldp-internal.h b/src/core/systemd/src/libsystemd-network/lldp-internal.h
deleted file mode 100644
index f23695f9..00000000
--- a/src/core/systemd/src/libsystemd-network/lldp-internal.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-event.h"
-#include "sd-lldp.h"
-
-#include "hashmap.h"
-#include "log.h"
-#include "prioq.h"
-
-struct sd_lldp {
-        unsigned n_ref;
-
-        int ifindex;
-        int fd;
-
-        sd_event *event;
-        int64_t event_priority;
-        sd_event_source *io_event_source;
-        sd_event_source *timer_event_source;
-
-        Prioq *neighbor_by_expiry;
-        Hashmap *neighbor_by_id;
-
-        uint64_t neighbors_max;
-
-        sd_lldp_callback_t callback;
-        void *userdata;
-
-        uint16_t capability_mask;
-
-        struct ether_addr filter_address;
-};
-
-#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
-#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
-
-const char* lldp_event_to_string(sd_lldp_event e) _const_;
-sd_lldp_event lldp_event_from_string(const char *s) _pure_;
diff --git a/src/core/systemd/src/libsystemd-network/lldp-neighbor.c b/src/core/systemd/src/libsystemd-network/lldp-neighbor.c
deleted file mode 100644
index 58ff0e0f..00000000
--- a/src/core/systemd/src/libsystemd-network/lldp-neighbor.c
+++ /dev/null
@@ -1,794 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include "alloc-util.h"
-#include "escape.h"
-#include "ether-addr-util.h"
-#include "hexdecoct.h"
-#include "in-addr-util.h"
-#include "lldp-internal.h"
-#include "lldp-neighbor.h"
-#include "memory-util.h"
-#include "missing_network.h"
-#include "unaligned.h"
-
-static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
-        siphash24_compress(id->chassis_id, id->chassis_id_size, state);
-        siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
-        siphash24_compress(id->port_id, id->port_id_size, state);
-        siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
-}
-
-int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
-        return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
-            ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
-}
-
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
-                                      sd_lldp_neighbor, lldp_neighbor_unlink);
-
-int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
-        const sd_lldp_neighbor *x = a, *y = b;
-
-        return CMP(x->until, y->until);
-}
-
-_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
-        if (!n)
-                return NULL;
-
-        assert(n->n_ref > 0 || n->lldp);
-        n->n_ref++;
-
-        return n;
-}
-
-static void lldp_neighbor_free(sd_lldp_neighbor *n) {
-        assert(n);
-
-        free(n->id.port_id);
-        free(n->id.chassis_id);
-        free(n->port_description);
-        free(n->system_name);
-        free(n->system_description);
-        free(n->mud_url);
-        free(n->chassis_id_as_string);
-        free(n->port_id_as_string);
-        free(n);
-}
-
-_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
-
-        /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
-         * the sd_lldp object. */
-
-        if (!n)
-                return NULL;
-
-        assert(n->n_ref > 0);
-        n->n_ref--;
-
-        if (n->n_ref <= 0 && !n->lldp)
-                lldp_neighbor_free(n);
-
-        return NULL;
-}
-
-sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
-
-        /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
-
-        if (!n)
-                return NULL;
-
-        if (!n->lldp)
-                return NULL;
-
-        /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
-         * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
-         * ourselves from the hashtable and sometimes are called after we already are de-registered. */
-
-        (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
-
-        assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
-
-        n->lldp = NULL;
-
-        if (n->n_ref <= 0)
-                lldp_neighbor_free(n);
-
-        return NULL;
-}
-
-sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
-        sd_lldp_neighbor *n;
-
-        n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
-        if (!n)
-                return NULL;
-
-        n->raw_size = raw_size;
-        n->n_ref = 1;
-
-        return n;
-}
-
-static int parse_string(char **s, const void *q, size_t n) {
-        const char *p = q;
-        char *k;
-
-        assert(s);
-        assert(p || n == 0);
-
-        if (*s) {
-                log_lldp("Found duplicate string, ignoring field.");
-                return 0;
-        }
-
-        /* Strip trailing NULs, just to be nice */
-        while (n > 0 && p[n-1] == 0)
-                n--;
-
-        if (n <= 0) /* Ignore empty strings */
-                return 0;
-
-        /* Look for inner NULs */
-        if (memchr(p, 0, n)) {
-                log_lldp("Found inner NUL in string, ignoring field.");
-                return 0;
-        }
-
-        /* Let's escape weird chars, for security reasons */
-        k = cescape_length(p, n);
-        if (!k)
-                return -ENOMEM;
-
-        free(*s);
-        *s = k;
-
-        return 1;
-}
-
-int lldp_neighbor_parse(sd_lldp_neighbor *n) {
-        struct ether_header h;
-        const uint8_t *p;
-        size_t left;
-        int r;
-
-        assert(n);
-
-        if (n->raw_size < sizeof(struct ether_header)) {
-                log_lldp("Received truncated packet, ignoring.");
-                return -EBADMSG;
-        }
-
-        memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
-
-        if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
-                log_lldp("Received packet with wrong type, ignoring.");
-                return -EBADMSG;
-        }
-
-        if (h.ether_dhost[0] != 0x01 ||
-            h.ether_dhost[1] != 0x80 ||
-            h.ether_dhost[2] != 0xc2 ||
-            h.ether_dhost[3] != 0x00 ||
-            h.ether_dhost[4] != 0x00 ||
-            !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
-                log_lldp("Received packet with wrong destination address, ignoring.");
-                return -EBADMSG;
-        }
-
-        memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
-        memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
-
-        p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
-        left = n->raw_size - sizeof(struct ether_header);
-
-        for (;;) {
-                uint8_t type;
-                uint16_t length;
-
-                if (left < 2) {
-                        log_lldp("TLV lacks header, ignoring.");
-                        return -EBADMSG;
-                }
-
-                type = p[0] >> 1;
-                length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
-                p += 2, left -= 2;
-
-                if (left < length) {
-                        log_lldp("TLV truncated, ignoring datagram.");
-                        return -EBADMSG;
-                }
-
-                switch (type) {
-
-                case SD_LLDP_TYPE_END:
-                        if (length != 0) {
-                                log_lldp("End marker TLV not zero-sized, ignoring datagram.");
-                                return -EBADMSG;
-                        }
-
-                        /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
-                         * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
-
-                        goto end_marker;
-
-                case SD_LLDP_TYPE_CHASSIS_ID:
-                        if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
-                                log_lldp("Chassis ID field size out of range, ignoring datagram.");
-                                return -EBADMSG;
-                        }
-                        if (n->id.chassis_id) {
-                                log_lldp("Duplicate chassis ID field, ignoring datagram.");
-                                return -EBADMSG;
-                        }
-
-                        n->id.chassis_id = memdup(p, length);
-                        if (!n->id.chassis_id)
-                                return -ENOMEM;
-
-                        n->id.chassis_id_size = length;
-                        break;
-
-                case SD_LLDP_TYPE_PORT_ID:
-                        if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
-                                log_lldp("Port ID field size out of range, ignoring datagram.");
-                                return -EBADMSG;
-                        }
-                        if (n->id.port_id) {
-                                log_lldp("Duplicate port ID field, ignoring datagram.");
-                                return -EBADMSG;
-                        }
-
-                        n->id.port_id = memdup(p, length);
-                        if (!n->id.port_id)
-                                return -ENOMEM;
-
-                        n->id.port_id_size = length;
-                        break;
-
-                case SD_LLDP_TYPE_TTL:
-                        if (length != 2) {
-                                log_lldp("TTL field has wrong size, ignoring datagram.");
-                                return -EBADMSG;
-                        }
-
-                        if (n->has_ttl) {
-                                log_lldp("Duplicate TTL field, ignoring datagram.");
-                                return -EBADMSG;
-                        }
-
-                        n->ttl = unaligned_read_be16(p);
-                        n->has_ttl = true;
-                        break;
-
-                case SD_LLDP_TYPE_PORT_DESCRIPTION:
-                        r = parse_string(&n->port_description, p, length);
-                        if (r < 0)
-                                return r;
-                        break;
-
-                case SD_LLDP_TYPE_SYSTEM_NAME:
-                        r = parse_string(&n->system_name, p, length);
-                        if (r < 0)
-                                return r;
-                        break;
-
-                case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
-                        r = parse_string(&n->system_description, p, length);
-                        if (r < 0)
-                                return r;
-                        break;
-
-                case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
-                        if (length != 4)
-                                log_lldp("System capabilities field has wrong size, ignoring.");
-                        else {
-                                n->system_capabilities = unaligned_read_be16(p);
-                                n->enabled_capabilities = unaligned_read_be16(p + 2);
-                                n->has_capabilities = true;
-                        }
-
-                        break;
-
-                case SD_LLDP_TYPE_PRIVATE: {
-                        if (length < 4)
-                                log_lldp("Found private TLV that is too short, ignoring.");
-                        else {
-                                /* RFC 8520: MUD URL */
-                                if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
-                                    p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
-                                        r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
-                                                         length - 1 - sizeof(SD_LLDP_OUI_MUD));
-                                        if (r < 0)
-                                                return r;
-                                }
-                        }
-                }
-
-                        break;
-                }
-
-                p += length, left -= length;
-        }
-
-end_marker:
-        if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
-                log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
-                return -EBADMSG;
-
-        }
-
-        n->rindex = sizeof(struct ether_header);
-
-        return 0;
-}
-
-void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
-        assert(n);
-
-        if (n->ttl > 0) {
-                usec_t base;
-
-                /* Use the packet's timestamp if there is one known */
-                base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
-                if (base <= 0 || base == USEC_INFINITY)
-                        base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
-
-                n->until = usec_add(base, n->ttl * USEC_PER_SEC);
-        } else
-                n->until = 0;
-
-        if (n->lldp)
-                prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
-}
-
-bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
-        if (a == b)
-                return true;
-
-        if (!a || !b)
-                return false;
-
-        if (a->raw_size != b->raw_size)
-                return false;
-
-        return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
-}
-
-_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
-        assert_return(n, -EINVAL);
-        assert_return(address, -EINVAL);
-
-        *address = n->source_address;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
-        assert_return(n, -EINVAL);
-        assert_return(address, -EINVAL);
-
-        *address = n->destination_address;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-        assert_return(size, -EINVAL);
-
-        *ret = LLDP_NEIGHBOR_RAW(n);
-        *size = n->raw_size;
-
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
-        assert_return(n, -EINVAL);
-        assert_return(type, -EINVAL);
-        assert_return(ret, -EINVAL);
-        assert_return(size, -EINVAL);
-
-        assert(n->id.chassis_id_size > 0);
-
-        *type = *(uint8_t*) n->id.chassis_id;
-        *ret = (uint8_t*) n->id.chassis_id + 1;
-        *size = n->id.chassis_id_size - 1;
-
-        return 0;
-}
-
-static int format_mac_address(const void *data, size_t sz, char **ret) {
-        struct ether_addr a;
-        char *k;
-
-        assert(data || sz <= 0);
-
-        if (sz != 7)
-                return 0;
-
-        memcpy(&a, (uint8_t*) data + 1, sizeof(a));
-
-        k = new(char, ETHER_ADDR_TO_STRING_MAX);
-        if (!k)
-                return -ENOMEM;
-
-        *ret = ether_addr_to_string(&a, k);
-        return 1;
-}
-
-static int format_network_address(const void *data, size_t sz, char **ret) {
-        union in_addr_union a;
-        int family, r;
-
-        if (sz == 6 && ((uint8_t*) data)[1] == 1) {
-                memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
-                family = AF_INET;
-        } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
-                memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
-                family = AF_INET6;
-        } else
-                return 0;
-
-        r = in_addr_to_string(family, &a, ret);
-        if (r < 0)
-                return r;
-        return 1;
-}
-
-_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
-        char *k;
-        int r;
-
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (n->chassis_id_as_string) {
-                *ret = n->chassis_id_as_string;
-                return 0;
-        }
-
-        assert(n->id.chassis_id_size > 0);
-
-        switch (*(uint8_t*) n->id.chassis_id) {
-
-        case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
-        case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
-        case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
-        case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
-        case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
-                k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
-                if (!k)
-                        return -ENOMEM;
-
-                goto done;
-
-        case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-                r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        goto done;
-
-                break;
-
-        case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
-                r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        goto done;
-
-                break;
-        }
-
-        /* Generic fallback */
-        k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
-        if (!k)
-                return -ENOMEM;
-
-done:
-        *ret = n->chassis_id_as_string = k;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
-        assert_return(n, -EINVAL);
-        assert_return(type, -EINVAL);
-        assert_return(ret, -EINVAL);
-        assert_return(size, -EINVAL);
-
-        assert(n->id.port_id_size > 0);
-
-        *type = *(uint8_t*) n->id.port_id;
-        *ret = (uint8_t*) n->id.port_id + 1;
-        *size = n->id.port_id_size - 1;
-
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
-        char *k;
-        int r;
-
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (n->port_id_as_string) {
-                *ret = n->port_id_as_string;
-                return 0;
-        }
-
-        assert(n->id.port_id_size > 0);
-
-        switch (*(uint8_t*) n->id.port_id) {
-
-        case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
-        case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
-        case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
-        case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-                k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
-                if (!k)
-                        return -ENOMEM;
-
-                goto done;
-
-        case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-                r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        goto done;
-
-                break;
-
-        case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
-                r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        goto done;
-
-                break;
-        }
-
-        /* Generic fallback */
-        k = hexmem(n->id.port_id, n->id.port_id_size);
-        if (!k)
-                return -ENOMEM;
-
-done:
-        *ret = n->port_id_as_string = k;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
-        assert_return(n, -EINVAL);
-        assert_return(ret_sec, -EINVAL);
-
-        *ret_sec = n->ttl;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (!n->system_name)
-                return -ENODATA;
-
-        *ret = n->system_name;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (!n->system_description)
-                return -ENODATA;
-
-        *ret = n->system_description;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (!n->port_description)
-                return -ENODATA;
-
-        *ret = n->port_description;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (!n->mud_url)
-                return -ENODATA;
-
-        *ret = n->mud_url;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (!n->has_capabilities)
-                return -ENODATA;
-
-        *ret = n->system_capabilities;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (!n->has_capabilities)
-                return -ENODATA;
-
-        *ret = n->enabled_capabilities;
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
-        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
-        int r;
-
-        assert_return(ret, -EINVAL);
-        assert_return(raw || raw_size <= 0, -EINVAL);
-
-        n = lldp_neighbor_new(raw_size);
-        if (!n)
-                return -ENOMEM;
-
-        memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
-        r = lldp_neighbor_parse(n);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(n);
-
-        return r;
-}
-
-_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
-        assert_return(n, -EINVAL);
-
-        assert(n->raw_size >= sizeof(struct ether_header));
-        n->rindex = sizeof(struct ether_header);
-
-        return n->rindex < n->raw_size;
-}
-
-_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
-        size_t length;
-
-        assert_return(n, -EINVAL);
-
-        if (n->rindex == n->raw_size) /* EOF */
-                return -ESPIPE;
-
-        if (n->rindex + 2 > n->raw_size) /* Truncated message */
-                return -EBADMSG;
-
-        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
-        if (n->rindex + 2 + length > n->raw_size)
-                return -EBADMSG;
-
-        n->rindex += 2 + length;
-        return n->rindex < n->raw_size;
-}
-
-_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
-        assert_return(n, -EINVAL);
-        assert_return(type, -EINVAL);
-
-        if (n->rindex == n->raw_size) /* EOF */
-                return -ESPIPE;
-
-        if (n->rindex + 2 > n->raw_size)
-                return -EBADMSG;
-
-        *type = LLDP_NEIGHBOR_TLV_TYPE(n);
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
-        uint8_t k;
-        int r;
-
-        assert_return(n, -EINVAL);
-
-        r = sd_lldp_neighbor_tlv_get_type(n, &k);
-        if (r < 0)
-                return r;
-
-        return type == k;
-}
-
-_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
-        const uint8_t *d;
-        size_t length;
-        int r;
-
-        assert_return(n, -EINVAL);
-        assert_return(oui, -EINVAL);
-        assert_return(subtype, -EINVAL);
-
-        r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -ENXIO;
-
-        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
-        if (length < 4)
-                return -EBADMSG;
-
-        if (n->rindex + 2 + length > n->raw_size)
-                return -EBADMSG;
-
-        d = LLDP_NEIGHBOR_TLV_DATA(n);
-        memcpy(oui, d, 3);
-        *subtype = d[3];
-
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
-        uint8_t k[3], st;
-        int r;
-
-        r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
-        if (r == -ENXIO)
-                return 0;
-        if (r < 0)
-                return r;
-
-        return memcmp(k, oui, 3) == 0 && st == subtype;
-}
-
-_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
-        size_t length;
-
-        assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
-        assert_return(size, -EINVAL);
-
-        /* Note that this returns the full TLV, including the TLV header */
-
-        if (n->rindex + 2 > n->raw_size)
-                return -EBADMSG;
-
-        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
-        if (n->rindex + 2 + length > n->raw_size)
-                return -EBADMSG;
-
-        *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
-        *size = length + 2;
-
-        return 0;
-}
-
-_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
-        assert_return(n, -EINVAL);
-        assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
-        assert_return(clock_supported(clock), -EOPNOTSUPP);
-        assert_return(ret, -EINVAL);
-
-        if (!triple_timestamp_is_set(&n->timestamp))
-                return -ENODATA;
-
-        *ret = triple_timestamp_by_clock(&n->timestamp, clock);
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/lldp-neighbor.h b/src/core/systemd/src/libsystemd-network/lldp-neighbor.h
deleted file mode 100644
index a5718c8c..00000000
--- a/src/core/systemd/src/libsystemd-network/lldp-neighbor.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <inttypes.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-#include "sd-lldp.h"
-
-#include "hash-funcs.h"
-#include "lldp-internal.h"
-#include "time-util.h"
-
-typedef struct LLDPNeighborID {
-        /* The spec calls this an "MSAP identifier" */
-        void *chassis_id;
-        size_t chassis_id_size;
-
-        void *port_id;
-        size_t port_id_size;
-} LLDPNeighborID;
-
-struct sd_lldp_neighbor {
-        /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
-        sd_lldp *lldp;
-        unsigned n_ref;
-
-        triple_timestamp timestamp;
-
-        usec_t until;
-        unsigned prioq_idx;
-
-        struct ether_addr source_address;
-        struct ether_addr destination_address;
-
-        LLDPNeighborID id;
-
-        /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
-        size_t raw_size;
-
-        /* The current read index for the iterative TLV interface */
-        size_t rindex;
-
-        /* And a couple of fields parsed out. */
-        bool has_ttl:1;
-        bool has_capabilities:1;
-        bool has_port_vlan_id:1;
-
-        uint16_t ttl;
-
-        uint16_t system_capabilities;
-        uint16_t enabled_capabilities;
-
-        char *port_description;
-        char *system_name;
-        char *system_description;
-        char *mud_url;
-
-        uint16_t port_vlan_id;
-
-        char *chassis_id_as_string;
-        char *port_id_as_string;
-};
-
-static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
-        return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
-}
-
-static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
-        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
-}
-
-static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
-        uint8_t *p;
-
-        p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
-        return p[1] + (((size_t) (p[0] & 1)) << 8);
-}
-
-static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
-        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
-}
-
-extern const struct hash_ops lldp_neighbor_hash_ops;
-int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
-int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
-
-sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
-sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
-int lldp_neighbor_parse(sd_lldp_neighbor *n);
-void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
-bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
diff --git a/src/core/systemd/src/libsystemd-network/lldp-network.c b/src/core/systemd/src/libsystemd-network/lldp-network.c
deleted file mode 100644
index 75dce58c..00000000
--- a/src/core/systemd/src/libsystemd-network/lldp-network.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <linux/filter.h>
-#include <netinet/if_ether.h>
-
-#include "fd-util.h"
-#include "lldp-network.h"
-#include "missing_network.h"
-#include "socket-util.h"
-
-int lldp_network_bind_raw_socket(int ifindex) {
-
-        static const struct sock_filter filter[] = {
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)),      /* A <- 4 bytes of destination MAC */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0),                    /* A != 01:80:c2:00 */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4),  /* A <- remaining 2 bytes of destination MAC */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0),                        /* A != 00:00 */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0),                        /* A != 00:03 */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0),                        /* A != 00:0e */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)),     /* A <- protocol */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0),                /* A != ETHERTYPE_LLDP */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-                BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1),                                 /* accept packet */
-        };
-
-        static const struct sock_fprog fprog = {
-                .len = ELEMENTSOF(filter),
-                .filter = (struct sock_filter*) filter,
-        };
-
-        struct packet_mreq mreq = {
-                .mr_ifindex = ifindex,
-                .mr_type = PACKET_MR_MULTICAST,
-                .mr_alen = ETH_ALEN,
-                .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
-        };
-
-        union sockaddr_union saddrll = {
-                .ll.sll_family = AF_PACKET,
-                .ll.sll_ifindex = ifindex,
-        };
-
-        _cleanup_close_ int fd = -1;
-        int r;
-
-        assert(ifindex > 0);
-
-        fd = socket(AF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
-                    htobe16(ETHERTYPE_LLDP));
-        if (fd < 0)
-                return -errno;
-
-        r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
-        if (r < 0)
-                return -errno;
-
-        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-        if (r < 0)
-                return -errno;
-
-        mreq.mr_address[ETH_ALEN - 1] = 0x03;
-        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-        if (r < 0)
-                return -errno;
-
-        mreq.mr_address[ETH_ALEN - 1] = 0x0E;
-        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-        if (r < 0)
-                return -errno;
-
-        r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
-        if (r < 0)
-                return -errno;
-
-        return TAKE_FD(fd);
-}
diff --git a/src/core/systemd/src/libsystemd-network/lldp-network.h b/src/core/systemd/src/libsystemd-network/lldp-network.h
deleted file mode 100644
index bc69b324..00000000
--- a/src/core/systemd/src/libsystemd-network/lldp-network.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-event.h"
-
-int lldp_network_bind_raw_socket(int ifindex);
diff --git a/src/core/systemd/src/libsystemd-network/network-internal.c b/src/core/systemd/src/libsystemd-network/network-internal.c
deleted file mode 100644
index efa3dcfa..00000000
--- a/src/core/systemd/src/libsystemd-network/network-internal.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <linux/if.h>
-#include <netinet/ether.h>
-
-#include "sd-ndisc.h"
-
-#include "alloc-util.h"
-#include "dhcp-lease-internal.h"
-#include "extract-word.h"
-#include "hexdecoct.h"
-#include "log.h"
-#include "network-internal.h"
-#include "parse-util.h"
-
-size_t serialize_in_addrs(FILE *f,
-                          const struct in_addr *addresses,
-                          size_t size,
-                          bool *with_leading_space,
-                          bool (*predicate)(const struct in_addr *addr)) {
-        assert(f);
-        assert(addresses);
-
-        size_t count = 0;
-        bool _space = false;
-        if (!with_leading_space)
-                with_leading_space = &_space;
-
-        for (size_t i = 0; i < size; i++) {
-                char sbuf[INET_ADDRSTRLEN];
-
-                if (predicate && !predicate(&addresses[i]))
-                        continue;
-
-                if (*with_leading_space)
-                        fputc(' ', f);
-                fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f);
-                count++;
-                *with_leading_space = true;
-        }
-
-        return count;
-}
-
-int deserialize_in_addrs(struct in_addr **ret, const char *string) {
-        _cleanup_free_ struct in_addr *addresses = NULL;
-        int size = 0;
-
-        assert(ret);
-        assert(string);
-
-        for (;;) {
-                _cleanup_free_ char *word = NULL;
-                struct in_addr *new_addresses;
-                int r;
-
-                r = extract_first_word(&string, &word, NULL, 0);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                new_addresses = reallocarray(addresses, size + 1, sizeof(struct in_addr));
-                if (!new_addresses)
-                        return -ENOMEM;
-                else
-                        addresses = new_addresses;
-
-                r = inet_pton(AF_INET, word, &(addresses[size]));
-                if (r <= 0)
-                        continue;
-
-                size++;
-        }
-
-        *ret = size > 0 ? TAKE_PTR(addresses) : NULL;
-
-        return size;
-}
-
-void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size, bool *with_leading_space) {
-        assert(f);
-        assert(addresses);
-        assert(size);
-
-        bool _space = false;
-        if (!with_leading_space)
-                with_leading_space = &_space;
-
-        for (size_t i = 0; i < size; i++) {
-                char buffer[INET6_ADDRSTRLEN];
-
-                if (*with_leading_space)
-                        fputc(' ', f);
-                fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
-                *with_leading_space = true;
-        }
-}
-
-int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
-        _cleanup_free_ struct in6_addr *addresses = NULL;
-        int size = 0;
-
-        assert(ret);
-        assert(string);
-
-        for (;;) {
-                _cleanup_free_ char *word = NULL;
-                struct in6_addr *new_addresses;
-                int r;
-
-                r = extract_first_word(&string, &word, NULL, 0);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                new_addresses = reallocarray(addresses, size + 1, sizeof(struct in6_addr));
-                if (!new_addresses)
-                        return -ENOMEM;
-                else
-                        addresses = new_addresses;
-
-                r = inet_pton(AF_INET6, word, &(addresses[size]));
-                if (r <= 0)
-                        continue;
-
-                size++;
-        }
-
-        *ret = TAKE_PTR(addresses);
-
-        return size;
-}
-
-void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
-        assert(f);
-        assert(key);
-        assert(routes);
-        assert(size);
-
-        fprintf(f, "%s=", key);
-
-        for (size_t i = 0; i < size; i++) {
-                char sbuf[INET_ADDRSTRLEN];
-                struct in_addr dest, gw;
-                uint8_t length;
-
-                assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0);
-                assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
-                assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
-
-                fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof sbuf), length);
-                fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof sbuf), i < size - 1 ? " ": "");
-        }
-
-        fputs("\n", f);
-}
-
-int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
-        _cleanup_free_ struct sd_dhcp_route *routes = NULL;
-        size_t size = 0, allocated = 0;
-
-        assert(ret);
-        assert(ret_size);
-        assert(ret_allocated);
-        assert(string);
-
-         /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
-        for (;;) {
-                _cleanup_free_ char *word = NULL;
-                char *tok, *tok_end;
-                unsigned n;
-                int r;
-
-                r = extract_first_word(&string, &word, NULL, 0);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                if (!GREEDY_REALLOC(routes, allocated, size + 1))
-                        return -ENOMEM;
-
-                tok = word;
-
-                /* get the subnet */
-                tok_end = strchr(tok, '/');
-                if (!tok_end)
-                        continue;
-                *tok_end = '\0';
-
-                r = inet_aton(tok, &routes[size].dst_addr);
-                if (r == 0)
-                        continue;
-
-                tok = tok_end + 1;
-
-                /* get the prefixlen */
-                tok_end = strchr(tok, ',');
-                if (!tok_end)
-                        continue;
-
-                *tok_end = '\0';
-
-                r = safe_atou(tok, &n);
-                if (r < 0 || n > 32)
-                        continue;
-
-                routes[size].dst_prefixlen = (uint8_t) n;
-                tok = tok_end + 1;
-
-                /* get the gateway */
-                r = inet_aton(tok, &routes[size].gw_addr);
-                if (r == 0)
-                        continue;
-
-                size++;
-        }
-
-        *ret_size = size;
-        *ret_allocated = allocated;
-        *ret = TAKE_PTR(routes);
-
-        return 0;
-}
-
-int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) {
-        _cleanup_free_ char *hex_buf = NULL;
-
-        assert(f);
-        assert(key);
-        assert(data);
-
-        hex_buf = hexmem(data, size);
-        if (!hex_buf)
-                return -ENOMEM;
-
-        fprintf(f, "%s=%s\n", key, hex_buf);
-
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/network-internal.h b/src/core/systemd/src/libsystemd-network/network-internal.h
deleted file mode 100644
index e5b853c0..00000000
--- a/src/core/systemd/src/libsystemd-network/network-internal.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <stdbool.h>
-#include <stdio.h>
-
-#include "sd-dhcp-lease.h"
-
-size_t serialize_in_addrs(FILE *f,
-                          const struct in_addr *addresses,
-                          size_t size,
-                          bool *with_leading_space,
-                          bool (*predicate)(const struct in_addr *addr));
-int deserialize_in_addrs(struct in_addr **addresses, const char *string);
-void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
-                         size_t size,
-                         bool *with_leading_space);
-int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
-
-/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
-struct sd_dhcp_route;
-struct sd_dhcp_lease;
-
-void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
-int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
-
-/* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */
-int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
-
-int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
-int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
diff --git a/src/core/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/core/systemd/src/libsystemd-network/sd-dhcp-client.c
deleted file mode 100644
index 27a3476f..00000000
--- a/src/core/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ /dev/null
@@ -1,2258 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <linux/if_infiniband.h>
-
-#include "sd-dhcp-client.h"
-
-#include "alloc-util.h"
-#include "dhcp-identifier.h"
-#include "dhcp-internal.h"
-#include "dhcp-lease-internal.h"
-#include "dhcp-protocol.h"
-#include "dns-domain.h"
-#include "event-util.h"
-#include "fd-util.h"
-#include "hostname-util.h"
-#include "io-util.h"
-#include "memory-util.h"
-#include "random-util.h"
-#include "set.h"
-#include "sort-util.h"
-#include "string-util.h"
-#include "strv.h"
-#include "time-util.h"
-#include "utf8.h"
-#include "web-util.h"
-
-#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN)  /* Arbitrary limit */
-#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
-
-#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
-#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
-
-#define TRANSIENT_FAILURE_ATTEMPTS 3 /* Arbitrary limit: how many attempts are considered enough to report
-                                      * transient failure. */
-
-typedef struct sd_dhcp_client_id {
-        uint8_t type;
-        union {
-                struct {
-                        /* 0: Generic (non-LL) (RFC 2132) */
-                        uint8_t data[MAX_CLIENT_ID_LEN];
-                } _packed_ gen;
-                struct {
-                        /* 1: Ethernet Link-Layer (RFC 2132) */
-                        uint8_t haddr[ETH_ALEN];
-                } _packed_ eth;
-                struct {
-                        /* 2 - 254: ARP/Link-Layer (RFC 2132) */
-                        uint8_t haddr[0];
-                } _packed_ ll;
-                struct {
-                        /* 255: Node-specific (RFC 4361) */
-                        be32_t iaid;
-                        struct duid duid;
-                } _packed_ ns;
-                struct {
-                        uint8_t data[MAX_CLIENT_ID_LEN];
-                } _packed_ raw;
-        };
-} _packed_ sd_dhcp_client_id;
-
-struct sd_dhcp_client {
-        unsigned n_ref;
-
-        DHCPState state;
-        sd_event *event;
-        int event_priority;
-        sd_event_source *timeout_resend;
-        int ifindex;
-        int fd;
-        uint16_t port;
-        union sockaddr_union link;
-        sd_event_source *receive_message;
-        bool request_broadcast;
-        Set *req_opts;
-        bool anonymize;
-        be32_t last_addr;
-        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
-        size_t mac_addr_len;
-        uint8_t bcast_addr[MAX_MAC_ADDR_LEN];
-        size_t bcast_addr_len;
-        uint16_t arp_type;
-        sd_dhcp_client_id client_id;
-        size_t client_id_len;
-        char *hostname;
-        char *vendor_class_identifier;
-        char *mudurl;
-        char **user_class;
-        uint32_t mtu;
-        uint32_t fallback_lease_lifetime;
-        uint32_t xid;
-        usec_t start_time;
-        usec_t t1_time;
-        usec_t t2_time;
-        usec_t expire_time;
-        uint64_t attempt;
-        uint64_t max_attempts;
-        OrderedHashmap *extra_options;
-        OrderedHashmap *vendor_options;
-        usec_t request_sent;
-        sd_event_source *timeout_t1;
-        sd_event_source *timeout_t2;
-        sd_event_source *timeout_expire;
-        sd_dhcp_client_callback_t callback;
-        void *userdata;
-        sd_dhcp_lease *lease;
-        usec_t start_delay;
-        int ip_service_type;
-};
-
-static const uint8_t default_req_opts[] = {
-        SD_DHCP_OPTION_SUBNET_MASK,
-        SD_DHCP_OPTION_ROUTER,
-        SD_DHCP_OPTION_HOST_NAME,
-        SD_DHCP_OPTION_DOMAIN_NAME,
-        SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
-};
-
-/* RFC7844 section 3:
-   MAY contain the Parameter Request List option.
-   RFC7844 section 3.6:
-   The client intending to protect its privacy SHOULD only request a
-   minimal number of options in the PRL and SHOULD also randomly shuffle
-   the ordering of option codes in the PRL.  If this random ordering
-   cannot be implemented, the client MAY order the option codes in the
-   PRL by option code number (lowest to highest).
-*/
-/* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */
-static const uint8_t default_req_opts_anonymize[] = {
-        SD_DHCP_OPTION_SUBNET_MASK,                     /* 1 */
-        SD_DHCP_OPTION_ROUTER,                          /* 3 */
-        SD_DHCP_OPTION_DOMAIN_NAME_SERVER,              /* 6 */
-        SD_DHCP_OPTION_DOMAIN_NAME,                     /* 15 */
-        SD_DHCP_OPTION_ROUTER_DISCOVER,                 /* 31 */
-        SD_DHCP_OPTION_STATIC_ROUTE,                    /* 33 */
-        SD_DHCP_OPTION_VENDOR_SPECIFIC,                 /* 43 */
-        SD_DHCP_OPTION_NETBIOS_NAMESERVER,              /* 44 */
-        SD_DHCP_OPTION_NETBIOS_NODETYPE,                /* 46 */
-        SD_DHCP_OPTION_NETBIOS_SCOPE,                   /* 47 */
-        SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,          /* 121 */
-        SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE,  /* 249 */
-        SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY,     /* 252 */
-};
-
-static int client_receive_message_raw(
-                sd_event_source *s,
-                int fd,
-                uint32_t revents,
-                void *userdata);
-static int client_receive_message_udp(
-                sd_event_source *s,
-                int fd,
-                uint32_t revents,
-                void *userdata);
-static void client_stop(sd_dhcp_client *client, int error);
-
-int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
-        const sd_dhcp_client_id *client_id = data;
-        _cleanup_free_ char *t = NULL;
-        int r = 0;
-
-        assert_return(data, -EINVAL);
-        assert_return(len >= 1, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        len -= 1;
-        if (len > MAX_CLIENT_ID_LEN)
-                return -EINVAL;
-
-        switch (client_id->type) {
-        case 0:
-                if (utf8_is_printable((char *) client_id->gen.data, len))
-                        r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
-                else
-                        r = asprintf(&t, "DATA");
-                break;
-        case 1:
-                if (len != sizeof_field(sd_dhcp_client_id, eth))
-                        return -EINVAL;
-
-                r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
-                             client_id->eth.haddr[0],
-                             client_id->eth.haddr[1],
-                             client_id->eth.haddr[2],
-                             client_id->eth.haddr[3],
-                             client_id->eth.haddr[4],
-                             client_id->eth.haddr[5]);
-                break;
-        case 2 ... 254:
-                r = asprintf(&t, "ARP/LL");
-                break;
-        case 255:
-                if (len < 6)
-                        return -EINVAL;
-
-                uint32_t iaid = be32toh(client_id->ns.iaid);
-                uint16_t duid_type = be16toh(client_id->ns.duid.type);
-                if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
-                        return -EINVAL;
-
-                r = asprintf(&t, "IAID:0x%x/DUID", iaid);
-                break;
-        }
-
-        if (r < 0)
-                return -ENOMEM;
-        *ret = TAKE_PTR(t);
-        return 0;
-}
-
-int sd_dhcp_client_set_callback(
-                sd_dhcp_client *client,
-                sd_dhcp_client_callback_t cb,
-                void *userdata) {
-
-        assert_return(client, -EINVAL);
-
-        client->callback = cb;
-        client->userdata = userdata;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
-        assert_return(client, -EINVAL);
-
-        client->request_broadcast = !!broadcast;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
-        assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
-
-        switch(option) {
-
-        case SD_DHCP_OPTION_PAD:
-        case SD_DHCP_OPTION_OVERLOAD:
-        case SD_DHCP_OPTION_MESSAGE_TYPE:
-        case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
-        case SD_DHCP_OPTION_END:
-                return -EINVAL;
-
-        default:
-                break;
-        }
-
-        return set_ensure_put(&client->req_opts, NULL, UINT8_TO_PTR(option));
-}
-
-int sd_dhcp_client_set_request_address(
-                sd_dhcp_client *client,
-                const struct in_addr *last_addr) {
-
-        assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
-
-        if (last_addr)
-                client->last_addr = last_addr->s_addr;
-        else
-                client->last_addr = INADDR_ANY;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
-
-        assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
-        assert_return(ifindex > 0, -EINVAL);
-
-        client->ifindex = ifindex;
-        return 0;
-}
-
-int sd_dhcp_client_set_mac(
-                sd_dhcp_client *client,
-                const uint8_t *addr,
-                const uint8_t *bcast_addr,
-                size_t addr_len,
-                uint16_t arp_type) {
-
-        DHCP_CLIENT_DONT_DESTROY(client);
-        bool need_restart = false;
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
-        assert_return(arp_type > 0, -EINVAL);
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(addr_len == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
-        else
-                return -EINVAL;
-
-        if (client->mac_addr_len == addr_len &&
-            memcmp(&client->mac_addr, addr, addr_len) == 0 &&
-            (client->bcast_addr_len > 0) == !!bcast_addr &&
-            (!bcast_addr || memcmp(&client->bcast_addr, bcast_addr, addr_len) == 0))
-                return 0;
-
-        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
-                log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
-                need_restart = true;
-                client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-        }
-
-        memcpy(&client->mac_addr, addr, addr_len);
-        client->mac_addr_len = addr_len;
-        client->arp_type = arp_type;
-        client->bcast_addr_len = 0;
-
-        if (bcast_addr) {
-                memcpy(&client->bcast_addr, bcast_addr, addr_len);
-                client->bcast_addr_len = addr_len;
-        }
-
-        if (need_restart && client->state != DHCP_STATE_STOPPED) {
-                r = sd_dhcp_client_start(client);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
-        }
-
-        return 0;
-}
-
-int sd_dhcp_client_get_client_id(
-                sd_dhcp_client *client,
-                uint8_t *type,
-                const uint8_t **data,
-                size_t *data_len) {
-
-        assert_return(client, -EINVAL);
-        assert_return(type, -EINVAL);
-        assert_return(data, -EINVAL);
-        assert_return(data_len, -EINVAL);
-
-        *type = 0;
-        *data = NULL;
-        *data_len = 0;
-        if (client->client_id_len) {
-                *type = client->client_id.type;
-                *data = client->client_id.raw.data;
-                *data_len = client->client_id_len - sizeof(client->client_id.type);
-        }
-
-        return 0;
-}
-
-int sd_dhcp_client_set_client_id(
-                sd_dhcp_client *client,
-                uint8_t type,
-                const uint8_t *data,
-                size_t data_len) {
-
-        DHCP_CLIENT_DONT_DESTROY(client);
-        bool need_restart = false;
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(data, -EINVAL);
-        assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
-        G_STATIC_ASSERT_EXPR (_NM_SD_MAX_CLIENT_ID_LEN == MAX_CLIENT_ID_LEN);
-
-        if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
-            client->client_id.type == type &&
-            memcmp(&client->client_id.raw.data, data, data_len) == 0)
-                return 0;
-
-        /* For hardware types, log debug message about unexpected data length.
-         *
-         * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
-         * the last 8 bytes of the address are stable and suitable to put into
-         * the client-id. The caller is advised to account for that. */
-        if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) ||
-            (type == ARPHRD_INFINIBAND && data_len != 8))
-                log_dhcp_client(client, "Changing client ID to hardware type %u with "
-                                "unexpected address length %zu",
-                                type, data_len);
-
-        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
-                log_dhcp_client(client, "Changing client ID on running DHCP "
-                                "client, restarting");
-                need_restart = true;
-                client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-        }
-
-        client->client_id.type = type;
-        memcpy(&client->client_id.raw.data, data, data_len);
-        client->client_id_len = data_len + sizeof (client->client_id.type);
-
-        if (need_restart && client->state != DHCP_STATE_STOPPED) {
-                r = sd_dhcp_client_start(client);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
-        }
-
-        return 0;
-}
-
-#if 0 /* NM_IGNORED */
-/**
- * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid
- * without further modification. Otherwise, if duid_type is supported, DUID
- * is set based on that type. Otherwise, an error is returned.
- */
-static int dhcp_client_set_iaid_duid_internal(
-                sd_dhcp_client *client,
-                bool iaid_append,
-                bool iaid_set,
-                uint32_t iaid,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len,
-                usec_t llt_time) {
-
-        DHCP_CLIENT_DONT_DESTROY(client);
-        int r;
-        size_t len;
-
-        assert_return(client, -EINVAL);
-        assert_return(duid_len == 0 || duid, -EINVAL);
-
-        if (duid) {
-                r = dhcp_validate_duid_len(duid_type, duid_len, true);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to validate length of DUID: %m");
-        }
-
-        zero(client->client_id);
-        client->client_id.type = 255;
-
-        if (iaid_append) {
-                if (iaid_set)
-                        client->client_id.ns.iaid = htobe32(iaid);
-                else {
-                        r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
-                                                     client->mac_addr_len,
-                                                     true,
-                                                     &client->client_id.ns.iaid);
-                        if (r < 0)
-                                return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
-                }
-        }
-
-        if (duid) {
-                client->client_id.ns.duid.type = htobe16(duid_type);
-                memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
-                len = sizeof(client->client_id.ns.duid.type) + duid_len;
-        } else
-                switch (duid_type) {
-                case DUID_TYPE_LLT:
-                        if (client->mac_addr_len == 0)
-                                return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
-
-                        r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
-                        if (r < 0)
-                                return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
-                        break;
-                case DUID_TYPE_EN:
-                        r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
-                        if (r < 0)
-                                return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
-                        break;
-                case DUID_TYPE_LL:
-                        if (client->mac_addr_len == 0)
-                                return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
-
-                        r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
-                        if (r < 0)
-                                return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
-                        break;
-                case DUID_TYPE_UUID:
-                        r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
-                        if (r < 0)
-                                return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
-                        break;
-                default:
-                        return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
-                }
-
-        client->client_id_len = sizeof(client->client_id.type) + len +
-                                (iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
-
-        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
-                log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
-                client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-                r = sd_dhcp_client_start(client);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
-        }
-
-        return 0;
-}
-
-int sd_dhcp_client_set_iaid_duid(
-                sd_dhcp_client *client,
-                bool iaid_set,
-                uint32_t iaid,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len) {
-        return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
-}
-
-int sd_dhcp_client_set_iaid_duid_llt(
-                sd_dhcp_client *client,
-                bool iaid_set,
-                uint32_t iaid,
-                usec_t llt_time) {
-        return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
-}
-
-int sd_dhcp_client_set_duid(
-                sd_dhcp_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len) {
-        return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
-}
-
-int sd_dhcp_client_set_duid_llt(
-                sd_dhcp_client *client,
-                usec_t llt_time) {
-        return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
-}
-#endif /* NM_IGNORED */
-
-int sd_dhcp_client_set_hostname(
-                sd_dhcp_client *client,
-                const char *hostname) {
-
-        assert_return(client, -EINVAL);
-
-        /* Make sure hostnames qualify as DNS and as Linux hostnames */
-        if (hostname &&
-            !(hostname_is_valid(hostname, 0) && dns_name_is_valid(hostname) > 0))
-                return -EINVAL;
-
-        return free_and_strdup(&client->hostname, hostname);
-}
-
-int sd_dhcp_client_set_vendor_class_identifier(
-                sd_dhcp_client *client,
-                const char *vci) {
-
-        assert_return(client, -EINVAL);
-
-        return free_and_strdup(&client->vendor_class_identifier, vci);
-}
-
-int sd_dhcp_client_set_mud_url(
-                sd_dhcp_client *client,
-                const char *mudurl) {
-
-        assert_return(client, -EINVAL);
-        assert_return(mudurl, -EINVAL);
-        assert_return(strlen(mudurl) <= 255, -EINVAL);
-        assert_return(http_url_is_valid(mudurl), -EINVAL);
-
-        return free_and_strdup(&client->mudurl, mudurl);
-}
-
-int sd_dhcp_client_set_user_class(
-                sd_dhcp_client *client,
-                const char* const* user_class) {
-
-        _cleanup_strv_free_ char **s = NULL;
-        char **p;
-
-        STRV_FOREACH(p, (char **) user_class)
-                if (strlen(*p) > 255)
-                        return -ENAMETOOLONG;
-
-        s = strv_copy((char **) user_class);
-        if (!s)
-                return -ENOMEM;
-
-        client->user_class = TAKE_PTR(s);
-
-        return 0;
-}
-
-int sd_dhcp_client_set_client_port(
-                sd_dhcp_client *client,
-                uint16_t port) {
-
-        assert_return(client, -EINVAL);
-
-        client->port = port;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
-        assert_return(client, -EINVAL);
-        assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
-
-        client->mtu = mtu;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
-        assert_return(client, -EINVAL);
-
-        client->max_attempts = max_attempts;
-
-        return 0;
-}
-
-int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(v, -EINVAL);
-
-        r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
-        if (r < 0)
-                return r;
-
-        sd_dhcp_option_ref(v);
-        return 0;
-}
-
-int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(v, -EINVAL);
-
-        r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops);
-        if (r < 0)
-                return -ENOMEM;
-
-        r = ordered_hashmap_put(client->vendor_options, v, v);
-        if (r < 0)
-                return r;
-
-        sd_dhcp_option_ref(v);
-
-        return 1;
-}
-
-int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
-        assert_return(client, -EINVAL);
-
-        if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
-                return -EADDRNOTAVAIL;
-
-        if (ret)
-                *ret = client->lease;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
-        assert_return(client, -EINVAL);
-
-        client->ip_service_type = type;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
-        assert_return(client, -EINVAL);
-        assert_return(fallback_lease_lifetime > 0, -EINVAL);
-
-        client->fallback_lease_lifetime = fallback_lease_lifetime;
-
-        return 0;
-}
-
-static int client_notify(sd_dhcp_client *client, int event) {
-        assert(client);
-
-        if (client->callback)
-                return client->callback(client, event, client->userdata);
-
-        return 0;
-}
-
-static int client_initialize(sd_dhcp_client *client) {
-        assert_return(client, -EINVAL);
-
-        client->receive_message = sd_event_source_unref(client->receive_message);
-
-        client->fd = safe_close(client->fd);
-
-        (void) event_source_disable(client->timeout_resend);
-        (void) event_source_disable(client->timeout_t1);
-        (void) event_source_disable(client->timeout_t2);
-        (void) event_source_disable(client->timeout_expire);
-
-        client->attempt = 0;
-
-        client->state = DHCP_STATE_INIT;
-        client->xid = 0;
-
-        client->lease = sd_dhcp_lease_unref(client->lease);
-
-        return 0;
-}
-
-static void client_stop(sd_dhcp_client *client, int error) {
-        assert(client);
-
-        if (error < 0)
-                log_dhcp_client_errno(client, error, "STOPPED: %m");
-        else if (error == SD_DHCP_CLIENT_EVENT_STOP)
-                log_dhcp_client(client, "STOPPED");
-        else
-                log_dhcp_client(client, "STOPPED: Unknown event");
-
-        client_notify(client, error);
-
-        client_initialize(client);
-}
-
-/* RFC2131 section 4.1:
- * retransmission delays should include -1 to +1 sec of random 'fuzz'. */
-#define RFC2131_RANDOM_FUZZ \
-        ((int64_t)(random_u64() % (2 * USEC_PER_SEC)) - (int64_t)USEC_PER_SEC)
-
-/* RFC2131 section 4.1:
- * for retransmission delays, timeout should start at 4s then double
- * each attempt with max of 64s, with -1 to +1 sec of random 'fuzz' added.
- * This assumes the first call will be using attempt 1. */
-static usec_t client_compute_request_timeout(usec_t now, uint64_t attempt) {
-        usec_t timeout = (UINT64_C(1) << MIN(attempt + 1, UINT64_C(6))) * USEC_PER_SEC;
-
-        return usec_sub_signed(usec_add(now, timeout), RFC2131_RANDOM_FUZZ);
-}
-
-/* RFC2131 section 4.4.5:
- * T1 defaults to (0.5 * duration_of_lease).
- * T2 defaults to (0.875 * duration_of_lease). */
-#define T1_DEFAULT(lifetime) ((lifetime) / 2)
-#define T2_DEFAULT(lifetime) (((lifetime) * 7) / 8)
-
-/* RFC2131 section 4.4.5:
- * the client SHOULD wait one-half of the remaining time until T2 (in RENEWING state)
- * and one-half of the remaining lease time (in REBINDING state), down to a minimum
- * of 60 seconds.
- * Note that while the default T1/T2 initial times do have random 'fuzz' applied,
- * the RFC sec 4.4.5 does not mention adding any fuzz to retries. */
-static usec_t client_compute_reacquisition_timeout(usec_t now, usec_t expire) {
-        return now + MAX(usec_sub_unsigned(expire, now) / 2, 60 * USEC_PER_SEC);
-}
-
-static int cmp_uint8(const uint8_t *a, const uint8_t *b) {
-        return CMP(*a, *b);
-}
-
-static int client_message_init(
-                sd_dhcp_client *client,
-                DHCPPacket **ret,
-                uint8_t type,
-                size_t *_optlen,
-                size_t *_optoffset) {
-
-        _cleanup_free_ DHCPPacket *packet = NULL;
-        size_t optlen, optoffset, size;
-        be16_t max_size;
-        usec_t time_now;
-        uint16_t secs;
-        int r;
-
-        assert(client);
-        assert(client->start_time);
-        assert(ret);
-        assert(_optlen);
-        assert(_optoffset);
-        assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE, DHCP_DECLINE));
-
-        optlen = DHCP_MIN_OPTIONS_SIZE;
-        size = sizeof(DHCPPacket) + optlen;
-
-        packet = malloc0(size);
-        if (!packet)
-                return -ENOMEM;
-
-        r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
-                              client->arp_type, optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
-           refuse to issue an DHCP lease if 'secs' is set to zero */
-        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0)
-                return r;
-        assert(time_now >= client->start_time);
-
-        /* seconds between sending first and last DISCOVER
-         * must always be strictly positive to deal with broken servers */
-        secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
-        packet->dhcp.secs = htobe16(secs);
-
-        /* RFC2132 section 4.1
-           A client that cannot receive unicast IP datagrams until its protocol
-           software has been configured with an IP address SHOULD set the
-           BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
-           DHCPREQUEST messages that client sends.  The BROADCAST bit will
-           provide a hint to the DHCP server and BOOTP relay agent to broadcast
-           any messages to the client on the client's subnet.
-
-           Note: some interfaces needs this to be enabled, but some networks
-           needs this to be disabled as broadcasts are filteretd, so this
-           needs to be configurable */
-        if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
-                packet->dhcp.flags = htobe16(0x8000);
-
-        /* RFC2132 section 4.1.1:
-           The client MUST include its hardware address in the ’chaddr’ field, if
-           necessary for delivery of DHCP reply messages.  Non-Ethernet
-           interfaces will leave 'chaddr' empty and use the client identifier
-           instead (eg, RFC 4390 section 2.1).
-         */
-        if (client->arp_type == ARPHRD_ETHER)
-                memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
-
-        /* If no client identifier exists, construct an RFC 4361-compliant one */
-        if (client->client_id_len == 0) {
-                size_t duid_len;
-
-                client->client_id.type = 255;
-
-                r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
-                                             true, &client->client_id.ns.iaid);
-                if (r < 0)
-                        return r;
-
-                r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
-                if (r < 0)
-                        return r;
-
-                client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
-        }
-
-        /* Some DHCP servers will refuse to issue an DHCP lease if the Client
-           Identifier option is not set */
-        if (client->client_id_len) {
-                r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_CLIENT_IDENTIFIER,
-                                       client->client_id_len,
-                                       &client->client_id);
-                if (r < 0)
-                        return r;
-        }
-
-        /* RFC2131 section 3.5:
-           in its initial DHCPDISCOVER or DHCPREQUEST message, a
-           client may provide the server with a list of specific
-           parameters the client is interested in. If the client
-           includes a list of parameters in a DHCPDISCOVER message,
-           it MUST include that list in any subsequent DHCPREQUEST
-           messages.
-         */
-
-        /* RFC7844 section 3:
-           MAY contain the Parameter Request List option. */
-        /* NOTE: in case that there would be an option to do not send
-         * any PRL at all, the size should be checked before sending */
-        if (!set_isempty(client->req_opts) && type != DHCP_RELEASE) {
-                _cleanup_free_ uint8_t *opts = NULL;
-                size_t n_opts, i = 0;
-                void *val;
-
-                n_opts = set_size(client->req_opts);
-                opts = new(uint8_t, n_opts);
-                if (!opts)
-                        return -ENOMEM;
-
-                SET_FOREACH(val, client->req_opts)
-                        opts[i++] = PTR_TO_UINT8(val);
-                assert(i == n_opts);
-
-                /* For anonymizing the request, let's sort the options. */
-                typesafe_qsort(opts, n_opts, cmp_uint8);
-
-                r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
-                                       n_opts, opts);
-                if (r < 0)
-                        return r;
-        }
-
-        /* RFC2131 section 3.5:
-           The client SHOULD include the ’maximum DHCP message size’ option to
-           let the server know how large the server may make its DHCP messages.
-
-           Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
-           than the defined default size unless the Maximum Message Size option
-           is explicitly set
-
-           RFC3442 "Requirements to Avoid Sizing Constraints":
-           Because a full routing table can be quite large, the standard 576
-           octet maximum size for a DHCP message may be too short to contain
-           some legitimate Classless Static Route options.  Because of this,
-           clients implementing the Classless Static Route option SHOULD send a
-           Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
-           stack is capable of receiving larger IP datagrams.  In this case, the
-           client SHOULD set the value of this option to at least the MTU of the
-           interface that the client is configuring.  The client MAY set the
-           value of this option higher, up to the size of the largest UDP packet
-           it is prepared to accept.  (Note that the value specified in the
-           Maximum DHCP Message Size option is the total maximum packet size,
-           including IP and UDP headers.)
-         */
-        /* RFC7844 section 3:
-           SHOULD NOT contain any other option. */
-        if (!client->anonymize && type != DHCP_RELEASE) {
-                max_size = htobe16(size);
-                r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
-                                       SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
-                                       2, &max_size);
-                if (r < 0)
-                        return r;
-        }
-
-        *_optlen = optlen;
-        *_optoffset = optoffset;
-        *ret = TAKE_PTR(packet);
-
-        return 0;
-}
-
-static int client_append_fqdn_option(
-                DHCPMessage *message,
-                size_t optlen,
-                size_t *optoffset,
-                const char *fqdn) {
-
-        uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
-        int r;
-
-        buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
-                    DHCP_FQDN_FLAG_E;  /* Canonical wire format */
-        buffer[1] = 0;                 /* RCODE1 (deprecated) */
-        buffer[2] = 0;                 /* RCODE2 (deprecated) */
-
-        r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
-        if (r > 0)
-                r = dhcp_option_append(message, optlen, optoffset, 0,
-                                       SD_DHCP_OPTION_FQDN, 3 + r, buffer);
-
-        return r;
-}
-
-static int dhcp_client_send_raw(
-                sd_dhcp_client *client,
-                DHCPPacket *packet,
-                size_t len) {
-
-        dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
-                                      INADDR_BROADCAST, DHCP_PORT_SERVER, len, client->ip_service_type);
-
-        return dhcp_network_send_raw_socket(client->fd, &client->link,
-                                            packet, len);
-}
-
-static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) {
-        sd_dhcp_option *j;
-        int r;
-
-        assert(client);
-
-        if (client->hostname) {
-                /* According to RFC 4702 "clients that send the Client FQDN option in
-                   their messages MUST NOT also send the Host Name option". Just send
-                   one of the two depending on the hostname type.
-                */
-                if (dns_name_is_single_label(client->hostname)) {
-                        /* it is unclear from RFC 2131 if client should send hostname in
-                           DHCPDISCOVER but dhclient does and so we do as well
-                        */
-                        r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
-                                               SD_DHCP_OPTION_HOST_NAME,
-                                               strlen(client->hostname), client->hostname);
-                } else
-                        r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset,
-                                                      client->hostname);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->vendor_class_identifier) {
-                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
-                                       SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
-                                       strlen(client->vendor_class_identifier),
-                                       client->vendor_class_identifier);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->mudurl) {
-                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
-                                       SD_DHCP_OPTION_MUD_URL,
-                                       strlen(client->mudurl),
-                                       client->mudurl);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->user_class) {
-                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
-                                       SD_DHCP_OPTION_USER_CLASS,
-                                       strv_length(client->user_class),
-                                       client->user_class);
-                if (r < 0)
-                        return r;
-        }
-
-        ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
-                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
-                                       j->option, j->length, j->data);
-                if (r < 0)
-                        return r;
-        }
-
-        if (!ordered_hashmap_isempty(client->vendor_options)) {
-                r = dhcp_option_append(
-                                &packet->dhcp, optlen, optoffset, 0,
-                                SD_DHCP_OPTION_VENDOR_SPECIFIC,
-                                ordered_hashmap_size(client->vendor_options), client->vendor_options);
-                if (r < 0)
-                        return r;
-        }
-
-
-        return 0;
-}
-
-static int client_send_discover(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *discover = NULL;
-        size_t optoffset, optlen;
-        int r;
-
-        assert(client);
-        assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
-
-        r = client_message_init(client, &discover, DHCP_DISCOVER,
-                                &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        /* the client may suggest values for the network address
-           and lease time in the DHCPDISCOVER message. The client may include
-           the ’requested IP address’ option to suggest that a particular IP
-           address be assigned, and may include the ’IP address lease time’
-           option to suggest the lease time it would like.
-         */
-        /* RFC7844 section 3:
-           SHOULD NOT contain any other option. */
-        if (!client->anonymize && client->last_addr != INADDR_ANY) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                       4, &client->last_addr);
-                if (r < 0)
-                        return r;
-        }
-
-        r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
-        if (r < 0)
-                return r;
-
-        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
-                               SD_DHCP_OPTION_END, 0, NULL);
-        if (r < 0)
-                return r;
-
-        /* We currently ignore:
-           The client SHOULD wait a random time between one and ten seconds to
-           desynchronize the use of DHCP at startup.
-         */
-        r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
-        if (r < 0)
-                return r;
-
-        log_dhcp_client(client, "DISCOVER");
-
-        return 0;
-}
-
-static int client_send_request(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *request = NULL;
-        size_t optoffset, optlen;
-        int r;
-
-        assert(client);
-
-        r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        switch (client->state) {
-        /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
-           SELECTING should be REQUESTING)
-         */
-
-        case DHCP_STATE_REQUESTING:
-                /* Client inserts the address of the selected server in ’server
-                   identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
-                   filled in with the yiaddr value from the chosen DHCPOFFER.
-                 */
-
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_SERVER_IDENTIFIER,
-                                       4, &client->lease->server_address);
-                if (r < 0)
-                        return r;
-
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                       4, &client->lease->address);
-                if (r < 0)
-                        return r;
-
-                break;
-
-        case DHCP_STATE_INIT_REBOOT:
-                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
-                   option MUST be filled in with client’s notion of its previously
-                   assigned address. ’ciaddr’ MUST be zero.
-                 */
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                       4, &client->last_addr);
-                if (r < 0)
-                        return r;
-                break;
-
-        case DHCP_STATE_RENEWING:
-                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
-                   option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
-                   client’s IP address.
-                */
-
-        case DHCP_STATE_REBINDING:
-                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
-                   option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
-                   client’s IP address.
-
-                   This message MUST be broadcast to the 0xffffffff IP broadcast address.
-                 */
-                request->dhcp.ciaddr = client->lease->address;
-
-                break;
-
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_SELECTING:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_BOUND:
-        case DHCP_STATE_STOPPED:
-                return -EINVAL;
-        }
-
-        r = client_append_common_discover_request_options(client, request, &optoffset, optlen);
-        if (r < 0)
-                return r;
-
-        r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                               SD_DHCP_OPTION_END, 0, NULL);
-        if (r < 0)
-                return r;
-
-        if (client->state == DHCP_STATE_RENEWING)
-                r = dhcp_network_send_udp_socket(client->fd,
-                                                 client->lease->server_address,
-                                                 DHCP_PORT_SERVER,
-                                                 &request->dhcp,
-                                                 sizeof(DHCPMessage) + optoffset);
-        else
-                r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
-        if (r < 0)
-                return r;
-
-        switch (client->state) {
-
-        case DHCP_STATE_REQUESTING:
-                log_dhcp_client(client, "REQUEST (requesting)");
-                break;
-
-        case DHCP_STATE_INIT_REBOOT:
-                log_dhcp_client(client, "REQUEST (init-reboot)");
-                break;
-
-        case DHCP_STATE_RENEWING:
-                log_dhcp_client(client, "REQUEST (renewing)");
-                break;
-
-        case DHCP_STATE_REBINDING:
-                log_dhcp_client(client, "REQUEST (rebinding)");
-                break;
-
-        default:
-                log_dhcp_client(client, "REQUEST (invalid)");
-                break;
-        }
-
-        return 0;
-}
-
-static int client_start(sd_dhcp_client *client);
-
-static int client_timeout_resend(
-                sd_event_source *s,
-                uint64_t usec,
-                void *userdata) {
-
-        sd_dhcp_client *client = userdata;
-        DHCP_CLIENT_DONT_DESTROY(client);
-        usec_t next_timeout;
-        uint64_t time_now;
-        int r;
-
-        assert(s);
-        assert(client);
-        assert(client->event);
-
-        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0)
-                goto error;
-
-        switch (client->state) {
-
-        case DHCP_STATE_RENEWING:
-                next_timeout = client_compute_reacquisition_timeout(time_now, client->t2_time);
-                break;
-
-        case DHCP_STATE_REBINDING:
-                next_timeout = client_compute_reacquisition_timeout(time_now, client->expire_time);
-                break;
-
-        case DHCP_STATE_REBOOTING:
-                /* start over as we did not receive a timely ack or nak */
-                r = client_initialize(client);
-                if (r < 0)
-                        goto error;
-
-                r = client_start(client);
-                if (r < 0)
-                        goto error;
-
-                log_dhcp_client(client, "REBOOTED");
-                return 0;
-
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_SELECTING:
-        case DHCP_STATE_REQUESTING:
-        case DHCP_STATE_BOUND:
-                if (client->attempt >= client->max_attempts)
-                        goto error;
-
-                client->attempt++;
-                next_timeout = client_compute_request_timeout(time_now, client->attempt);
-                break;
-
-        case DHCP_STATE_STOPPED:
-                r = -EINVAL;
-                goto error;
-
-        default:
-                assert_not_reached("Unhandled choice");
-        }
-
-        r = event_reset_time(client->event, &client->timeout_resend,
-                             clock_boottime_or_monotonic(),
-                             next_timeout, 10 * USEC_PER_MSEC,
-                             client_timeout_resend, client,
-                             client->event_priority, "dhcp4-resend-timer", true);
-        if (r < 0)
-                goto error;
-
-        switch (client->state) {
-        case DHCP_STATE_INIT:
-                r = client_send_discover(client);
-                if (r >= 0) {
-                        client->state = DHCP_STATE_SELECTING;
-                        client->attempt = 0;
-                } else if (client->attempt >= client->max_attempts)
-                        goto error;
-
-                break;
-
-        case DHCP_STATE_SELECTING:
-                r = client_send_discover(client);
-                if (r < 0 && client->attempt >= client->max_attempts)
-                        goto error;
-
-                break;
-
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REQUESTING:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-                r = client_send_request(client);
-                if (r < 0 && client->attempt >= client->max_attempts)
-                         goto error;
-
-                if (client->state == DHCP_STATE_INIT_REBOOT)
-                        client->state = DHCP_STATE_REBOOTING;
-
-                client->request_sent = time_now;
-                break;
-
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_BOUND:
-                break;
-
-        case DHCP_STATE_STOPPED:
-                r = -EINVAL;
-                goto error;
-        }
-
-        if (client->attempt >= TRANSIENT_FAILURE_ATTEMPTS)
-                client_notify(client, SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE);
-
-        return 0;
-
-error:
-        client_stop(client, r);
-
-        /* Errors were dealt with when stopping the client, don't spill
-           errors into the event loop handler */
-        return 0;
-}
-
-static int client_initialize_io_events(
-                sd_dhcp_client *client,
-                sd_event_io_handler_t io_callback) {
-
-        int r;
-
-        assert(client);
-        assert(client->event);
-
-        r = sd_event_add_io(client->event, &client->receive_message,
-                            client->fd, EPOLLIN, io_callback,
-                            client);
-        if (r < 0)
-                goto error;
-
-        r = sd_event_source_set_priority(client->receive_message,
-                                         client->event_priority);
-        if (r < 0)
-                goto error;
-
-        r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
-        if (r < 0)
-                goto error;
-
-error:
-        if (r < 0)
-                client_stop(client, r);
-
-        return 0;
-}
-
-static int client_initialize_time_events(sd_dhcp_client *client) {
-        uint64_t usec = 0;
-        int r;
-
-        assert(client);
-        assert(client->event);
-
-        if (client->start_delay > 0) {
-                assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
-                usec += client->start_delay;
-        }
-
-        r = event_reset_time(client->event, &client->timeout_resend,
-                             clock_boottime_or_monotonic(),
-                             usec, 0,
-                             client_timeout_resend, client,
-                             client->event_priority, "dhcp4-resend-timer", true);
-        if (r < 0)
-                client_stop(client, r);
-
-        return 0;
-
-}
-
-static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) {
-        client_initialize_io_events(client, io_callback);
-        client_initialize_time_events(client);
-
-        return 0;
-}
-
-static int client_start_delayed(sd_dhcp_client *client) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->event, -EINVAL);
-        assert_return(client->ifindex > 0, -EINVAL);
-        assert_return(client->fd < 0, -EBUSY);
-        assert_return(client->xid == 0, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY);
-
-        client->xid = random_u32();
-
-        r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
-                                         client->mac_addr, client->mac_addr_len,
-                                         client->bcast_addr, client->bcast_addr_len,
-                                         client->arp_type, client->port);
-        if (r < 0) {
-                client_stop(client, r);
-                return r;
-        }
-        client->fd = r;
-
-        if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT))
-                client->start_time = now(clock_boottime_or_monotonic());
-
-        return client_initialize_events(client, client_receive_message_raw);
-}
-
-static int client_start(sd_dhcp_client *client) {
-        client->start_delay = 0;
-        return client_start_delayed(client);
-}
-
-static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_dhcp_client *client = userdata;
-        DHCP_CLIENT_DONT_DESTROY(client);
-
-        log_dhcp_client(client, "EXPIRED");
-
-        client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
-
-        /* lease was lost, start over if not freed or stopped in callback */
-        if (client->state != DHCP_STATE_STOPPED) {
-                client_initialize(client);
-                client_start(client);
-        }
-
-        return 0;
-}
-
-static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_dhcp_client *client = userdata;
-        DHCP_CLIENT_DONT_DESTROY(client);
-        int r;
-
-        assert(client);
-
-        client->receive_message = sd_event_source_unref(client->receive_message);
-        client->fd = safe_close(client->fd);
-
-        client->state = DHCP_STATE_REBINDING;
-        client->attempt = 0;
-
-        r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
-                                         client->mac_addr, client->mac_addr_len,
-                                         client->bcast_addr, client->bcast_addr_len,
-                                         client->arp_type, client->port);
-        if (r < 0) {
-                client_stop(client, r);
-                return 0;
-        }
-        client->fd = r;
-
-        return client_initialize_events(client, client_receive_message_raw);
-}
-
-static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_dhcp_client *client = userdata;
-        DHCP_CLIENT_DONT_DESTROY(client);
-
-        if (client->lease)
-                client->state = DHCP_STATE_RENEWING;
-        else if (client->state != DHCP_STATE_INIT)
-                client->state = DHCP_STATE_INIT_REBOOT;
-        client->attempt = 0;
-
-        return client_initialize_time_events(client);
-}
-
-static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) {
-        _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
-        int r;
-
-        r = dhcp_lease_new(&lease);
-        if (r < 0)
-                return r;
-
-        if (client->client_id_len) {
-                r = dhcp_lease_set_client_id(lease,
-                                             (uint8_t *) &client->client_id,
-                                             client->client_id_len);
-                if (r < 0)
-                        return r;
-        }
-
-        r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
-        if (r != DHCP_OFFER) {
-                log_dhcp_client(client, "received message was not an OFFER, ignoring");
-                return -ENOMSG;
-        }
-
-        lease->next_server = offer->siaddr;
-        lease->address = offer->yiaddr;
-
-        if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
-                lease->lifetime = client->fallback_lease_lifetime;
-
-        if (lease->address == 0 ||
-            lease->server_address == 0 ||
-            lease->lifetime == 0) {
-                log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
-                return -ENOMSG;
-        }
-
-        if (!lease->have_subnet_mask) {
-                r = dhcp_lease_set_default_subnet_mask(lease);
-                if (r < 0) {
-                        log_dhcp_client(client,
-                                        "received lease lacks subnet mask, "
-                                        "and a fallback one cannot be generated, ignoring");
-                        return -ENOMSG;
-                }
-        }
-
-        sd_dhcp_lease_unref(client->lease);
-        client->lease = TAKE_PTR(lease);
-
-        if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0)
-                return -ENOMSG;
-
-        log_dhcp_client(client, "OFFER");
-
-        return 0;
-}
-
-static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) {
-        int r;
-
-        r = dhcp_option_parse(force, len, NULL, NULL, NULL);
-        if (r != DHCP_FORCERENEW)
-                return -ENOMSG;
-
-        log_dhcp_client(client, "FORCERENEW");
-
-        return 0;
-}
-
-static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
-        if (a->address != b->address)
-                return false;
-
-        if (a->subnet_mask != b->subnet_mask)
-                return false;
-
-        if (a->router_size != b->router_size)
-                return false;
-
-        for (size_t i = 0; i < a->router_size; i++)
-                if (a->router[i].s_addr != b->router[i].s_addr)
-                        return false;
-
-        return true;
-}
-
-static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
-        _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
-        _cleanup_free_ char *error_message = NULL;
-        int r;
-
-        r = dhcp_lease_new(&lease);
-        if (r < 0)
-                return r;
-
-        if (client->client_id_len) {
-                r = dhcp_lease_set_client_id(lease,
-                                             (uint8_t *) &client->client_id,
-                                             client->client_id_len);
-                if (r < 0)
-                        return r;
-        }
-
-        r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
-        if (r == DHCP_NAK) {
-                log_dhcp_client(client, "NAK: %s", strna(error_message));
-                return -EADDRNOTAVAIL;
-        }
-
-        if (r != DHCP_ACK) {
-                log_dhcp_client(client, "received message was not an ACK, ignoring");
-                return -ENOMSG;
-        }
-
-        lease->next_server = ack->siaddr;
-
-        lease->address = ack->yiaddr;
-
-        if (lease->address == INADDR_ANY ||
-            lease->server_address == INADDR_ANY ||
-            lease->lifetime == 0) {
-                log_dhcp_client(client, "received lease lacks address, server "
-                                "address or lease lifetime, ignoring");
-                return -ENOMSG;
-        }
-
-        if (lease->subnet_mask == INADDR_ANY) {
-                r = dhcp_lease_set_default_subnet_mask(lease);
-                if (r < 0) {
-                        log_dhcp_client(client,
-                                        "received lease lacks subnet mask, "
-                                        "and a fallback one cannot be generated, ignoring");
-                        return -ENOMSG;
-                }
-        }
-
-        r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
-        if (client->lease) {
-                if (lease_equal(client->lease, lease))
-                        r = SD_DHCP_CLIENT_EVENT_RENEW;
-                else
-                        r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
-
-                client->lease = sd_dhcp_lease_unref(client->lease);
-        }
-
-        client->lease = TAKE_PTR(lease);
-
-        log_dhcp_client(client, "ACK");
-
-        return r;
-}
-
-static int client_set_lease_timeouts(sd_dhcp_client *client) {
-        usec_t time_now;
-        char time_string[FORMAT_TIMESPAN_MAX];
-        int r;
-
-        assert(client);
-        assert(client->event);
-        assert(client->lease);
-        assert(client->lease->lifetime);
-
-        /* don't set timers for infinite leases */
-        if (client->lease->lifetime == 0xffffffff) {
-                (void) event_source_disable(client->timeout_t1);
-                (void) event_source_disable(client->timeout_t2);
-                (void) event_source_disable(client->timeout_expire);
-
-                return 0;
-        }
-
-        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0)
-                return r;
-        assert(client->request_sent <= time_now);
-
-        /* verify that 0 < t2 < lifetime */
-        if (client->lease->t2 == 0 || client->lease->t2 >= client->lease->lifetime)
-                client->lease->t2 = T2_DEFAULT(client->lease->lifetime);
-        /* verify that 0 < t1 < lifetime */
-        if (client->lease->t1 == 0 || client->lease->t1 >= client->lease->t2)
-                client->lease->t1 = T1_DEFAULT(client->lease->lifetime);
-        /* now, if t1 >= t2, t1 *must* be T1_DEFAULT, since the previous check
-         * could not evalate to false if t1 >= t2; so setting t2 to T2_DEFAULT
-         * guarantees t1 < t2. */
-        if (client->lease->t1 >= client->lease->t2)
-                client->lease->t2 = T2_DEFAULT(client->lease->lifetime);
-
-        client->expire_time = client->request_sent + client->lease->lifetime * USEC_PER_SEC;
-        client->t1_time = client->request_sent + client->lease->t1 * USEC_PER_SEC;
-        client->t2_time = client->request_sent + client->lease->t2 * USEC_PER_SEC;
-
-        /* RFC2131 section 4.4.5:
-         * Times T1 and T2 SHOULD be chosen with some random "fuzz".
-         * Since the RFC doesn't specify here the exact 'fuzz' to use,
-         * we use the range from section 4.1: -1 to +1 sec. */
-        client->t1_time = usec_sub_signed(client->t1_time, RFC2131_RANDOM_FUZZ);
-        client->t2_time = usec_sub_signed(client->t2_time, RFC2131_RANDOM_FUZZ);
-
-        /* after fuzzing, ensure t2 is still >= t1 */
-        client->t2_time = MAX(client->t1_time, client->t2_time);
-
-        /* arm lifetime timeout */
-        r = event_reset_time(client->event, &client->timeout_expire,
-                             clock_boottime_or_monotonic(),
-                             client->expire_time, 10 * USEC_PER_MSEC,
-                             client_timeout_expire, client,
-                             client->event_priority, "dhcp4-lifetime", true);
-        if (r < 0)
-                return r;
-
-        /* don't arm earlier timeouts if this has already expired */
-        if (client->expire_time <= time_now)
-                return 0;
-
-        log_dhcp_client(client, "lease expires in %s",
-                        format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->expire_time - time_now, USEC_PER_SEC));
-
-        /* arm T2 timeout */
-        r = event_reset_time(client->event, &client->timeout_t2,
-                             clock_boottime_or_monotonic(),
-                             client->t2_time, 10 * USEC_PER_MSEC,
-                             client_timeout_t2, client,
-                             client->event_priority, "dhcp4-t2-timeout", true);
-        if (r < 0)
-                return r;
-
-        /* don't arm earlier timeout if this has already expired */
-        if (client->t2_time <= time_now)
-                return 0;
-
-        log_dhcp_client(client, "T2 expires in %s",
-                        format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t2_time - time_now, USEC_PER_SEC));
-
-        /* arm T1 timeout */
-        r = event_reset_time(client->event, &client->timeout_t1,
-                             clock_boottime_or_monotonic(),
-                             client->t1_time, 10 * USEC_PER_MSEC,
-                             client_timeout_t1, client,
-                             client->event_priority, "dhcp4-t1-timer", true);
-        if (r < 0)
-                return r;
-
-        if (client->t1_time > time_now)
-                log_dhcp_client(client, "T1 expires in %s",
-                                format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t1_time - time_now, USEC_PER_SEC));
-
-        return 0;
-}
-
-static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
-        DHCP_CLIENT_DONT_DESTROY(client);
-        char time_string[FORMAT_TIMESPAN_MAX];
-        int r = 0, notify_event = 0;
-
-        assert(client);
-        assert(client->event);
-        assert(message);
-
-        switch (client->state) {
-        case DHCP_STATE_SELECTING:
-
-                r = client_handle_offer(client, message, len);
-                if (r >= 0) {
-
-                        client->state = DHCP_STATE_REQUESTING;
-                        client->attempt = 0;
-
-                        r = event_reset_time(client->event, &client->timeout_resend,
-                                             clock_boottime_or_monotonic(),
-                                             0, 0,
-                                             client_timeout_resend, client,
-                                             client->event_priority, "dhcp4-resend-timer", true);
-                        if (r < 0)
-                                goto error;
-                } else if (r == -ENOMSG)
-                        /* invalid message, let's ignore it */
-                        return 0;
-
-                break;
-
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_REQUESTING:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-
-                r = client_handle_ack(client, message, len);
-                if (r >= 0) {
-                        client->start_delay = 0;
-                        (void) event_source_disable(client->timeout_resend);
-                        client->receive_message =
-                                sd_event_source_unref(client->receive_message);
-                        client->fd = safe_close(client->fd);
-
-                        if (IN_SET(client->state, DHCP_STATE_REQUESTING,
-                                   DHCP_STATE_REBOOTING))
-                                notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
-                        else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
-                                notify_event = r;
-
-                        client->state = DHCP_STATE_BOUND;
-                        client->attempt = 0;
-
-                        client->last_addr = client->lease->address;
-
-                        r = client_set_lease_timeouts(client);
-                        if (r < 0) {
-                                log_dhcp_client(client, "could not set lease timeouts");
-                                goto error;
-                        }
-
-                        r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
-                        if (r < 0) {
-                                log_dhcp_client(client, "could not bind UDP socket");
-                                goto error;
-                        }
-
-                        client->fd = r;
-
-                        client_initialize_io_events(client, client_receive_message_udp);
-
-                        if (notify_event) {
-                                client_notify(client, notify_event);
-                                if (client->state == DHCP_STATE_STOPPED)
-                                        return 0;
-                        }
-
-                } else if (r == -EADDRNOTAVAIL) {
-                        /* got a NAK, let's restart the client */
-                        client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
-
-                        r = client_initialize(client);
-                        if (r < 0)
-                                goto error;
-
-                        r = client_start_delayed(client);
-                        if (r < 0)
-                                goto error;
-
-                        log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX,
-                                                                                client->start_delay, USEC_PER_SEC));
-
-                        client->start_delay = CLAMP(client->start_delay * 2,
-                                                    RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
-
-                        return 0;
-                } else if (r == -ENOMSG)
-                        /* invalid message, let's ignore it */
-                        return 0;
-
-                break;
-
-        case DHCP_STATE_BOUND:
-                r = client_handle_forcerenew(client, message, len);
-                if (r >= 0) {
-                        r = client_timeout_t1(NULL, 0, client);
-                        if (r < 0)
-                                goto error;
-                } else if (r == -ENOMSG)
-                        /* invalid message, let's ignore it */
-                        return 0;
-
-                break;
-
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_INIT_REBOOT:
-
-                break;
-
-        case DHCP_STATE_STOPPED:
-                r = -EINVAL;
-                goto error;
-        }
-
-error:
-        if (r < 0)
-                client_stop(client, r);
-
-        return r;
-}
-
-static int client_receive_message_udp(
-                sd_event_source *s,
-                int fd,
-                uint32_t revents,
-                void *userdata) {
-
-        sd_dhcp_client *client = userdata;
-        _cleanup_free_ DHCPMessage *message = NULL;
-        const uint8_t *expected_chaddr = NULL;
-        uint8_t expected_hlen = 0;
-        ssize_t len, buflen;
-
-        assert(s);
-        assert(client);
-
-        buflen = next_datagram_size_fd(fd);
-        if (buflen == -ENETDOWN)
-                /* the link is down. Don't return an error or the I/O event
-                   source will be disconnected and we won't be able to receive
-                   packets again when the link comes back. */
-                return 0;
-        if (buflen < 0)
-                return buflen;
-
-        message = malloc0(buflen);
-        if (!message)
-                return -ENOMEM;
-
-        len = recv(fd, message, buflen, 0);
-        if (len < 0) {
-                /* see comment above for why we shouldn't error out on ENETDOWN. */
-                if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
-                        return 0;
-
-                return log_dhcp_client_errno(client, errno,
-                                             "Could not receive message from UDP socket: %m");
-        }
-        if ((size_t) len < sizeof(DHCPMessage)) {
-                log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
-                return 0;
-        }
-
-        if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
-                log_dhcp_client(client, "Not a DHCP message: ignoring");
-                return 0;
-        }
-
-        if (message->op != BOOTREPLY) {
-                log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
-                return 0;
-        }
-
-        if (message->htype != client->arp_type) {
-                log_dhcp_client(client, "Packet type does not match client type");
-                return 0;
-        }
-
-        if (client->arp_type == ARPHRD_ETHER) {
-                expected_hlen = ETH_ALEN;
-                expected_chaddr = &client->mac_addr[0];
-        }
-
-        if (message->hlen != expected_hlen) {
-                log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
-                return 0;
-        }
-
-        if (expected_hlen > 0 && memcmp(&message->chaddr[0], expected_chaddr, expected_hlen)) {
-                log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
-                return 0;
-        }
-
-        if (client->state != DHCP_STATE_BOUND &&
-            be32toh(message->xid) != client->xid) {
-                /* in BOUND state, we may receive FORCERENEW with xid set by server,
-                   so ignore the xid in this case */
-                log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
-                                be32toh(message->xid), client->xid);
-                return 0;
-        }
-
-        return client_handle_message(client, message, len);
-}
-
-static int client_receive_message_raw(
-                sd_event_source *s,
-                int fd,
-                uint32_t revents,
-                void *userdata) {
-
-        sd_dhcp_client *client = userdata;
-        _cleanup_free_ DHCPPacket *packet = NULL;
-        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control;
-        struct iovec iov = {};
-        struct msghdr msg = {
-                .msg_iov = &iov,
-                .msg_iovlen = 1,
-                .msg_control = &control,
-                .msg_controllen = sizeof(control),
-        };
-        struct cmsghdr *cmsg;
-        bool checksum = true;
-        ssize_t buflen, len;
-        int r;
-
-        assert(s);
-        assert(client);
-
-        buflen = next_datagram_size_fd(fd);
-        if (buflen == -ENETDOWN)
-                return 0;
-        if (buflen < 0)
-                return buflen;
-
-        packet = malloc0(buflen);
-        if (!packet)
-                return -ENOMEM;
-
-        iov = IOVEC_MAKE(packet, buflen);
-
-        len = recvmsg_safe(fd, &msg, 0);
-        if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN))
-                return 0;
-        if (len < 0)
-                return log_dhcp_client_errno(client, len,
-                                             "Could not receive message from raw socket: %m");
-
-        if ((size_t) len < sizeof(DHCPPacket))
-                return 0;
-
-        cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata)));
-        if (cmsg) {
-                struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg);
-                checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
-        }
-
-        r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
-        if (r < 0)
-                return 0;
-
-        len -= DHCP_IP_UDP_SIZE;
-
-        return client_handle_message(client, &packet->dhcp, len);
-}
-
-int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
-        assert_return(client, -EINVAL);
-        assert_return(client->fd >= 0, -EINVAL);
-
-        if (!client->lease)
-                return 0;
-
-        client->start_delay = 0;
-        client->attempt = 1;
-        client->state = DHCP_STATE_RENEWING;
-
-        return client_initialize_time_events(client);
-}
-
-int sd_dhcp_client_start(sd_dhcp_client *client) {
-        int r;
-
-        assert_return(client, -EINVAL);
-
-        r = client_initialize(client);
-        if (r < 0)
-                return r;
-
-        /* RFC7844 section 3.3:
-           SHOULD perform a complete four-way handshake, starting with a
-           DHCPDISCOVER, to obtain a new address lease.  If the client can
-           ascertain that this is exactly the same network to which it was
-           previously connected, and if the link-layer address did not change,
-           the client MAY issue a DHCPREQUEST to try to reclaim the current
-           address. */
-        if (client->last_addr && !client->anonymize)
-                client->state = DHCP_STATE_INIT_REBOOT;
-
-        r = client_start(client);
-        if (r >= 0)
-                log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex);
-
-        return r;
-}
-
-int sd_dhcp_client_send_release(sd_dhcp_client *client) {
-        assert_return(client, -EINVAL);
-        assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
-        assert_return(client->lease, -EUNATCH);
-
-        _cleanup_free_ DHCPPacket *release = NULL;
-        size_t optoffset, optlen;
-        int r;
-
-        r = client_message_init(client, &release, DHCP_RELEASE, &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        /* Fill up release IP and MAC */
-        release->dhcp.ciaddr = client->lease->address;
-        memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
-
-        r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
-                               SD_DHCP_OPTION_END, 0, NULL);
-        if (r < 0)
-                return r;
-
-        r = dhcp_network_send_udp_socket(client->fd,
-                                         client->lease->server_address,
-                                         DHCP_PORT_SERVER,
-                                         &release->dhcp,
-                                         sizeof(DHCPMessage) + optoffset);
-        if (r < 0)
-                return r;
-
-        log_dhcp_client(client, "RELEASE");
-
-        return 0;
-}
-
-int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
-        assert_return(client, -EINVAL);
-        assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
-        assert_return(client->lease, -EUNATCH);
-
-        _cleanup_free_ DHCPPacket *release = NULL;
-        size_t optoffset, optlen;
-        int r;
-
-        r = client_message_init(client, &release, DHCP_DECLINE, &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        release->dhcp.ciaddr = client->lease->address;
-        memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
-
-        r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
-                               SD_DHCP_OPTION_END, 0, NULL);
-        if (r < 0)
-                return r;
-
-        r = dhcp_network_send_udp_socket(client->fd,
-                                         client->lease->server_address,
-                                         DHCP_PORT_SERVER,
-                                         &release->dhcp,
-                                         sizeof(DHCPMessage) + optoffset);
-        if (r < 0)
-                return r;
-
-        log_dhcp_client(client, "DECLINE");
-
-        client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-
-        if (client->state != DHCP_STATE_STOPPED) {
-                r = sd_dhcp_client_start(client);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-int sd_dhcp_client_stop(sd_dhcp_client *client) {
-        if (!client)
-                return 0;
-
-        DHCP_CLIENT_DONT_DESTROY(client);
-
-        client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-        client->state = DHCP_STATE_STOPPED;
-
-        return 0;
-}
-
-int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(!client->event, -EBUSY);
-
-        if (event)
-                client->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&client->event);
-                if (r < 0)
-                        return 0;
-        }
-
-        client->event_priority = priority;
-
-        return 0;
-}
-
-int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
-        assert_return(client, -EINVAL);
-
-        client->event = sd_event_unref(client->event);
-
-        return 0;
-}
-
-sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
-        assert_return(client, NULL);
-
-        return client->event;
-}
-
-static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
-        if (!client)
-                return NULL;
-
-        log_dhcp_client(client, "FREE");
-
-        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-        client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
-        client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
-        client->timeout_expire = sd_event_source_unref(client->timeout_expire);
-
-        client_initialize(client);
-
-        sd_dhcp_client_detach_event(client);
-
-        sd_dhcp_lease_unref(client->lease);
-
-        set_free(client->req_opts);
-        free(client->hostname);
-        free(client->vendor_class_identifier);
-        free(client->mudurl);
-        client->user_class = strv_free(client->user_class);
-        ordered_hashmap_free(client->extra_options);
-        ordered_hashmap_free(client->vendor_options);
-        return mfree(client);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free);
-
-int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
-        const uint8_t *opts;
-        size_t n_opts;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = new(sd_dhcp_client, 1);
-        if (!client)
-                return -ENOMEM;
-
-        *client = (sd_dhcp_client) {
-                .n_ref = 1,
-                .state = DHCP_STATE_INIT,
-                .ifindex = -1,
-                .fd = -1,
-                .mtu = DHCP_DEFAULT_MIN_SIZE,
-                .port = DHCP_PORT_CLIENT,
-                .anonymize = !!anonymize,
-                .max_attempts = (uint64_t) -1,
-                .ip_service_type = -1,
-        };
-        /* NOTE: this could be moved to a function. */
-        if (anonymize) {
-                n_opts = ELEMENTSOF(default_req_opts_anonymize);
-                opts = default_req_opts_anonymize;
-        } else {
-                n_opts = ELEMENTSOF(default_req_opts);
-                opts = default_req_opts;
-        }
-
-        for (size_t i = 0; i < n_opts; i++) {
-                r = sd_dhcp_client_set_request_option(client, opts[i]);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret = TAKE_PTR(client);
-
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c
deleted file mode 100644
index 94b0d35c..00000000
--- a/src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ /dev/null
@@ -1,1401 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "sd-dhcp-lease.h"
-
-#include "alloc-util.h"
-#include "dhcp-lease-internal.h"
-#include "dhcp-protocol.h"
-#include "dns-domain.h"
-#include "env-file.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "hexdecoct.h"
-#include "hostname-util.h"
-#include "in-addr-util.h"
-#include "network-internal.h"
-#include "parse-util.h"
-#include "stdio-util.h"
-#include "string-util.h"
-#include "strv.h"
-#include "tmpfile-util.h"
-#include "unaligned.h"
-
-int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->address == 0)
-                return -ENODATA;
-
-        addr->s_addr = lease->address;
-        return 0;
-}
-
-int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (!lease->have_broadcast)
-                return -ENODATA;
-
-        addr->s_addr = lease->broadcast;
-        return 0;
-}
-
-int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
-        assert_return(lease, -EINVAL);
-        assert_return(lifetime, -EINVAL);
-
-        if (lease->lifetime <= 0)
-                return -ENODATA;
-
-        *lifetime = lease->lifetime;
-        return 0;
-}
-
-int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
-        assert_return(lease, -EINVAL);
-        assert_return(t1, -EINVAL);
-
-        if (lease->t1 <= 0)
-                return -ENODATA;
-
-        *t1 = lease->t1;
-        return 0;
-}
-
-int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
-        assert_return(lease, -EINVAL);
-        assert_return(t2, -EINVAL);
-
-        if (lease->t2 <= 0)
-                return -ENODATA;
-
-        *t2 = lease->t2;
-        return 0;
-}
-
-int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
-        assert_return(lease, -EINVAL);
-        assert_return(mtu, -EINVAL);
-
-        if (lease->mtu <= 0)
-                return -ENODATA;
-
-        *mtu = lease->mtu;
-        return 0;
-}
-
-int sd_dhcp_lease_get_servers(
-                sd_dhcp_lease *lease,
-                sd_dhcp_lease_server_type what,
-                const struct in_addr **addr) {
-
-        assert_return(lease, -EINVAL);
-        assert_return(what >= 0, -EINVAL);
-        assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->servers[what].size <= 0)
-                return -ENODATA;
-
-        *addr = lease->servers[what].addr;
-        return (int) lease->servers[what].size;
-}
-
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr);
-}
-int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr);
-}
-int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
-}
-int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
-}
-int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
-}
-int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
-}
-
-int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
-        assert_return(lease, -EINVAL);
-        assert_return(domainname, -EINVAL);
-
-        if (!lease->domainname)
-                return -ENODATA;
-
-        *domainname = lease->domainname;
-        return 0;
-}
-
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
-        assert_return(lease, -EINVAL);
-        assert_return(hostname, -EINVAL);
-
-        if (!lease->hostname)
-                return -ENODATA;
-
-        *hostname = lease->hostname;
-        return 0;
-}
-
-int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
-        assert_return(lease, -EINVAL);
-        assert_return(root_path, -EINVAL);
-
-        if (!lease->root_path)
-                return -ENODATA;
-
-        *root_path = lease->root_path;
-        return 0;
-}
-
-int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->router_size <= 0)
-                return -ENODATA;
-
-        *addr = lease->router;
-        return (int) lease->router_size;
-}
-
-int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (!lease->have_subnet_mask)
-                return -ENODATA;
-
-        addr->s_addr = lease->subnet_mask;
-        return 0;
-}
-
-int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->server_address == 0)
-                return -ENODATA;
-
-        addr->s_addr = lease->server_address;
-        return 0;
-}
-
-int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->next_server == 0)
-                return -ENODATA;
-
-        addr->s_addr = lease->next_server;
-        return 0;
-}
-
-/*
- * The returned routes array must be freed by the caller.
- * Route objects have the same lifetime of the lease and must not be freed.
- */
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
-        sd_dhcp_route **ret;
-        unsigned i;
-
-        assert_return(lease, -EINVAL);
-        assert_return(routes, -EINVAL);
-
-        if (lease->static_route_size <= 0)
-                return -ENODATA;
-
-        ret = new(sd_dhcp_route *, lease->static_route_size);
-        if (!ret)
-                return -ENOMEM;
-
-        for (i = 0; i < lease->static_route_size; i++)
-                ret[i] = &lease->static_route[i];
-
-        *routes = ret;
-        return (int) lease->static_route_size;
-}
-
-int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
-        size_t r;
-
-        assert_return(lease, -EINVAL);
-        assert_return(domains, -EINVAL);
-
-        r = strv_length(lease->search_domains);
-        if (r > 0) {
-                *domains = lease->search_domains;
-                return (int) r;
-        }
-
-        return -ENODATA;
-}
-
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
-        assert_return(lease, -EINVAL);
-        assert_return(data, -EINVAL);
-        assert_return(data_len, -EINVAL);
-
-        if (lease->vendor_specific_len <= 0)
-                return -ENODATA;
-
-        *data = lease->vendor_specific;
-        *data_len = lease->vendor_specific_len;
-        return 0;
-}
-
-static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
-        assert(lease);
-
-        while (lease->private_options) {
-                struct sd_dhcp_raw_option *option = lease->private_options;
-
-                LIST_REMOVE(options, lease->private_options, option);
-
-                free(option->data);
-                free(option);
-        }
-
-        free(lease->root_path);
-        free(lease->router);
-        free(lease->timezone);
-        free(lease->hostname);
-        free(lease->domainname);
-
-        for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
-                free(lease->servers[i].addr);
-
-        free(lease->static_route);
-        free(lease->client_id);
-        free(lease->vendor_specific);
-        strv_free(lease->search_domains);
-        return mfree(lease);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_lease, sd_dhcp_lease, dhcp_lease_free);
-
-static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
-        assert(option);
-        assert(ret);
-
-        if (len != 4)
-                return -EINVAL;
-
-        *ret = unaligned_read_be32((be32_t*) option);
-        if (*ret < min)
-                *ret = min;
-
-        return 0;
-}
-
-static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
-        assert(option);
-        assert(ret);
-
-        if (len != 2)
-                return -EINVAL;
-
-        *ret = unaligned_read_be16((be16_t*) option);
-        if (*ret < min)
-                *ret = min;
-
-        return 0;
-}
-
-static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
-        assert(option);
-        assert(ret);
-
-        if (len != 4)
-                return -EINVAL;
-
-        memcpy(ret, option, 4);
-        return 0;
-}
-
-static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
-        assert(option);
-        assert(ret);
-
-        if (len <= 0)
-                *ret = mfree(*ret);
-        else {
-                char *string;
-
-                /*
-                 * One trailing NUL byte is OK, we don't mind. See:
-                 * https://github.com/systemd/systemd/issues/1337
-                 */
-                if (memchr(option, 0, len - 1))
-                        return -EINVAL;
-
-                string = memdup_suffix0((const char *) option, len);
-                if (!string)
-                        return -ENOMEM;
-
-                free_and_replace(*ret, string);
-        }
-
-        return 0;
-}
-
-static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
-        _cleanup_free_ char *name = NULL, *normalized = NULL;
-        int r;
-
-        assert(option);
-        assert(ret);
-
-        r = lease_parse_string(option, len, &name);
-        if (r < 0)
-                return r;
-        if (!name) {
-                *ret = mfree(*ret);
-                return 0;
-        }
-
-        r = dns_name_normalize(name, 0, &normalized);
-        if (r < 0)
-                return r;
-
-        if (is_localhost(normalized))
-                return -EINVAL;
-
-        if (dns_name_is_root(normalized))
-                return -EINVAL;
-
-        free_and_replace(*ret, normalized);
-
-        return 0;
-}
-
-static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
-        assert(option || len == 0);
-        assert(ret);
-        assert(n_ret);
-
-        if (len <= 0) {
-                *ret = mfree(*ret);
-                *n_ret = 0;
-        } else {
-                size_t n_addresses;
-                struct in_addr *addresses;
-
-                if (len % 4 != 0)
-                        return -EINVAL;
-
-                n_addresses = len / 4;
-
-                addresses = newdup(struct in_addr, option, n_addresses);
-                if (!addresses)
-                        return -ENOMEM;
-
-                free(*ret);
-                *ret = addresses;
-                *n_ret = n_addresses;
-        }
-
-        return 0;
-}
-
-static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
-        assert(option || len == 0);
-        assert(ret);
-        assert(n_ret);
-
-        if (len <= 0)
-                return -EINVAL;
-
-        /* The SIP record is like the other, regular server records, but prefixed with a single "encoding"
-         * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like
-         * the other fields */
-
-        if (option[0] != 1) { /* We only support IP address encoding for now */
-                *ret = mfree(*ret);
-                *n_ret = 0;
-                return 0;
-        }
-
-        return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret);
-}
-
-static int lease_parse_routes(
-                const uint8_t *option, size_t len,
-                struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
-
-        struct in_addr addr;
-
-        assert(option || len <= 0);
-        assert(routes);
-        assert(routes_size);
-        assert(routes_allocated);
-
-        if (len <= 0)
-                return 0;
-
-        if (len % 8 != 0)
-                return -EINVAL;
-
-        if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
-                return -ENOMEM;
-
-        while (len >= 8) {
-                struct sd_dhcp_route *route = *routes + *routes_size;
-                int r;
-
-                route->option = SD_DHCP_OPTION_STATIC_ROUTE;
-                r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
-                if (r < 0) {
-                        log_debug("Failed to determine destination prefix length from class based IP, ignoring");
-                        continue;
-                }
-
-                assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
-                route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
-                option += 4;
-
-                assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
-                option += 4;
-
-                len -= 8;
-                (*routes_size)++;
-        }
-
-        return 0;
-}
-
-/* parses RFC3442 Classless Static Route Option */
-static int lease_parse_classless_routes(
-                const uint8_t *option, size_t len,
-                struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
-
-        assert(option || len <= 0);
-        assert(routes);
-        assert(routes_size);
-        assert(routes_allocated);
-
-        if (len <= 0)
-                return 0;
-
-        /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)*  */
-
-        while (len > 0) {
-                uint8_t dst_octets;
-                struct sd_dhcp_route *route;
-
-                if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
-                        return -ENOMEM;
-
-                route = *routes + *routes_size;
-                route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE;
-
-                dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
-                route->dst_prefixlen = *option;
-                option++;
-                len--;
-
-                /* can't have more than 4 octets in IPv4 */
-                if (dst_octets > 4 || len < dst_octets)
-                        return -EINVAL;
-
-                route->dst_addr.s_addr = 0;
-                memcpy(&route->dst_addr.s_addr, option, dst_octets);
-                option += dst_octets;
-                len -= dst_octets;
-
-                if (len < 4)
-                        return -EINVAL;
-
-                assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
-                option += 4;
-                len -= 4;
-
-                (*routes_size)++;
-        }
-
-        return 0;
-}
-
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
-        sd_dhcp_lease *lease = userdata;
-        int r;
-
-        assert(lease);
-
-        switch(code) {
-
-        case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
-                r = lease_parse_u32(option, len, &lease->lifetime, 1);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
-
-                break;
-
-        case SD_DHCP_OPTION_SERVER_IDENTIFIER:
-                r = lease_parse_be32(option, len, &lease->server_address);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
-
-                break;
-
-        case SD_DHCP_OPTION_SUBNET_MASK:
-                r = lease_parse_be32(option, len, &lease->subnet_mask);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
-                else
-                        lease->have_subnet_mask = true;
-                break;
-
-        case SD_DHCP_OPTION_BROADCAST:
-                r = lease_parse_be32(option, len, &lease->broadcast);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
-                else
-                        lease->have_broadcast = true;
-                break;
-
-        case SD_DHCP_OPTION_ROUTER:
-                r = lease_parse_in_addrs(option, len, &lease->router, &lease->router_size);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse router addresses, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_NTP_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_SIP_SERVER:
-                r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse SIP server, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_POP3_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_SMTP_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_LPR_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse LPR server, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_STATIC_ROUTE:
-                r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_INTERFACE_MTU:
-                r = lease_parse_u16(option, len, &lease->mtu, 68);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
-                if (lease->mtu < DHCP_DEFAULT_MIN_SIZE) {
-                        log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_DEFAULT_MIN_SIZE);
-                        lease->mtu = DHCP_DEFAULT_MIN_SIZE;
-                }
-
-                break;
-
-        case SD_DHCP_OPTION_DOMAIN_NAME:
-                r = lease_parse_domain(option, len, &lease->domainname);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST:
-                r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_HOST_NAME:
-                r = lease_parse_domain(option, len, &lease->hostname);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        case SD_DHCP_OPTION_ROOT_PATH:
-                r = lease_parse_string(option, len, &lease->root_path);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse root path, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_RENEWAL_T1_TIME:
-                r = lease_parse_u32(option, len, &lease->t1, 1);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_REBINDING_T2_TIME:
-                r = lease_parse_u32(option, len, &lease->t2, 1);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
-                r = lease_parse_classless_routes(
-                                option, len,
-                                &lease->static_route,
-                                &lease->static_route_size,
-                                &lease->static_route_allocated);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
-                break;
-
-        case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: {
-                _cleanup_free_ char *tz = NULL;
-
-                r = lease_parse_string(option, len, &tz);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
-                        return 0;
-                }
-
-                if (!timezone_is_valid(tz, LOG_DEBUG)) {
-                        log_debug_errno(r, "Timezone is not valid, ignoring: %m");
-                        return 0;
-                }
-
-                free_and_replace(lease->timezone, tz);
-
-                break;
-        }
-
-        case SD_DHCP_OPTION_VENDOR_SPECIFIC:
-
-                if (len <= 0)
-                        lease->vendor_specific = mfree(lease->vendor_specific);
-                else {
-                        void *p;
-
-                        p = memdup(option, len);
-                        if (!p)
-                                return -ENOMEM;
-
-                        free(lease->vendor_specific);
-                        lease->vendor_specific = p;
-                }
-
-                lease->vendor_specific_len = len;
-                break;
-
-        case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
-                r = dhcp_lease_insert_private_option(lease, code, option, len);
-                if (r < 0)
-                        return r;
-
-                break;
-
-        default:
-                log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
-                break;
-        }
-
-        return 0;
-}
-
-/* Parses compressed domain names. */
-int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains) {
-        _cleanup_strv_free_ char **names = NULL;
-        size_t pos = 0, cnt = 0;
-        int r;
-
-        assert(domains);
-        assert_return(option && len > 0, -ENODATA);
-
-        while (pos < len) {
-                _cleanup_free_ char *name = NULL;
-                size_t n = 0, allocated = 0;
-                size_t jump_barrier = pos, next_chunk = 0;
-                bool first = true;
-
-                for (;;) {
-                        uint8_t c;
-                        c = option[pos++];
-
-                        if (c == 0) {
-                                /* End of name */
-                                break;
-                        } else if (c <= 63) {
-                                const char *label;
-
-                                /* Literal label */
-                                label = (const char*) (option + pos);
-                                pos += c;
-                                if (pos >= len)
-                                        return -EBADMSG;
-
-                                if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
-                                        return -ENOMEM;
-
-                                if (first)
-                                        first = false;
-                                else
-                                        name[n++] = '.';
-
-                                r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
-                                if (r < 0)
-                                        return r;
-
-                                n += r;
-                        } else if (FLAGS_SET(c, 0xc0)) {
-                                /* Pointer */
-
-                                uint8_t d;
-                                uint16_t ptr;
-
-                                if (pos >= len)
-                                        return -EBADMSG;
-
-                                d = option[pos++];
-                                ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
-
-                                /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
-                                if (ptr >= jump_barrier)
-                                        return -EBADMSG;
-                                jump_barrier = ptr;
-
-                                /* Save current location so we don't end up re-parsing what's parsed so far. */
-                                if (next_chunk == 0)
-                                        next_chunk = pos;
-
-                                pos = ptr;
-                        } else
-                                return -EBADMSG;
-                }
-
-                if (!GREEDY_REALLOC(name, allocated, n + 1))
-                        return -ENOMEM;
-                name[n] = 0;
-
-                r = strv_extend(&names, name);
-                if (r < 0)
-                        return r;
-
-                cnt++;
-
-                if (next_chunk != 0)
-                      pos = next_chunk;
-        }
-
-        *domains = TAKE_PTR(names);
-
-        return cnt;
-}
-
-int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
-        struct sd_dhcp_raw_option *cur, *option;
-
-        assert(lease);
-
-        LIST_FOREACH(options, cur, lease->private_options) {
-                if (tag < cur->tag)
-                        break;
-                if (tag == cur->tag) {
-                        log_debug("Ignoring duplicate option, tagged %i.", tag);
-                        return 0;
-                }
-        }
-
-        option = new(struct sd_dhcp_raw_option, 1);
-        if (!option)
-                return -ENOMEM;
-
-        option->tag = tag;
-        option->length = len;
-        option->data = memdup(data, len);
-        if (!option->data) {
-                free(option);
-                return -ENOMEM;
-        }
-
-        LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
-        return 0;
-}
-
-int dhcp_lease_new(sd_dhcp_lease **ret) {
-        sd_dhcp_lease *lease;
-
-        lease = new0(sd_dhcp_lease, 1);
-        if (!lease)
-                return -ENOMEM;
-
-        lease->n_ref = 1;
-
-        *ret = lease;
-        return 0;
-}
-
-int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
-        _cleanup_free_ char *temp_path = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
-        struct sd_dhcp_raw_option *option;
-        struct in_addr address;
-        const struct in_addr *addresses;
-        const void *client_id, *data;
-        size_t client_id_len, data_len;
-        char sbuf[INET_ADDRSTRLEN];
-        const char *string;
-        uint16_t mtu;
-        _cleanup_free_ sd_dhcp_route **routes = NULL;
-        char **search_domains = NULL;
-        uint32_t t1, t2, lifetime;
-        int r;
-
-        assert(lease);
-        assert(lease_file);
-
-        r = fopen_temporary(lease_file, &f, &temp_path);
-        if (r < 0)
-                goto fail;
-
-        (void) fchmod(fileno(f), 0644);
-
-        fprintf(f,
-                "# This is private data. Do not parse.\n");
-
-        r = sd_dhcp_lease_get_address(lease, &address);
-        if (r >= 0)
-                fprintf(f, "ADDRESS=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf)));
-
-        r = sd_dhcp_lease_get_netmask(lease, &address);
-        if (r >= 0)
-                fprintf(f, "NETMASK=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf)));
-
-        r = sd_dhcp_lease_get_router(lease, &addresses);
-        if (r > 0) {
-                fputs("ROUTER=", f);
-                serialize_in_addrs(f, addresses, r, false, NULL);
-                fputc('\n', f);
-        }
-
-        r = sd_dhcp_lease_get_server_identifier(lease, &address);
-        if (r >= 0)
-                fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf)));
-
-        r = sd_dhcp_lease_get_next_server(lease, &address);
-        if (r >= 0)
-                fprintf(f, "NEXT_SERVER=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf)));
-
-        r = sd_dhcp_lease_get_broadcast(lease, &address);
-        if (r >= 0)
-                fprintf(f, "BROADCAST=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf)));
-
-        r = sd_dhcp_lease_get_mtu(lease, &mtu);
-        if (r >= 0)
-                fprintf(f, "MTU=%" PRIu16 "\n", mtu);
-
-        r = sd_dhcp_lease_get_t1(lease, &t1);
-        if (r >= 0)
-                fprintf(f, "T1=%" PRIu32 "\n", t1);
-
-        r = sd_dhcp_lease_get_t2(lease, &t2);
-        if (r >= 0)
-                fprintf(f, "T2=%" PRIu32 "\n", t2);
-
-        r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
-        if (r >= 0)
-                fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime);
-
-        r = sd_dhcp_lease_get_dns(lease, &addresses);
-        if (r > 0) {
-                fputs("DNS=", f);
-                serialize_in_addrs(f, addresses, r, false, NULL);
-                fputc('\n', f);
-        }
-
-        r = sd_dhcp_lease_get_ntp(lease, &addresses);
-        if (r > 0) {
-                fputs("NTP=", f);
-                serialize_in_addrs(f, addresses, r, false, NULL);
-                fputc('\n', f);
-        }
-
-        r = sd_dhcp_lease_get_sip(lease, &addresses);
-        if (r > 0) {
-                fputs("SIP=", f);
-                serialize_in_addrs(f, addresses, r, false, NULL);
-                fputc('\n', f);
-        }
-
-        r = sd_dhcp_lease_get_domainname(lease, &string);
-        if (r >= 0)
-                fprintf(f, "DOMAINNAME=%s\n", string);
-
-        r = sd_dhcp_lease_get_search_domains(lease, &search_domains);
-        if (r > 0) {
-                fputs("DOMAIN_SEARCH_LIST=", f);
-                fputstrv(f, search_domains, NULL, NULL);
-                fputc('\n', f);
-        }
-
-        r = sd_dhcp_lease_get_hostname(lease, &string);
-        if (r >= 0)
-                fprintf(f, "HOSTNAME=%s\n", string);
-
-        r = sd_dhcp_lease_get_root_path(lease, &string);
-        if (r >= 0)
-                fprintf(f, "ROOT_PATH=%s\n", string);
-
-        r = sd_dhcp_lease_get_routes(lease, &routes);
-        if (r > 0)
-                serialize_dhcp_routes(f, "ROUTES", routes, r);
-
-        r = sd_dhcp_lease_get_timezone(lease, &string);
-        if (r >= 0)
-                fprintf(f, "TIMEZONE=%s\n", string);
-
-        r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
-        if (r >= 0) {
-                _cleanup_free_ char *client_id_hex = NULL;
-
-                client_id_hex = hexmem(client_id, client_id_len);
-                if (!client_id_hex) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-                fprintf(f, "CLIENTID=%s\n", client_id_hex);
-        }
-
-        r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
-        if (r >= 0) {
-                _cleanup_free_ char *option_hex = NULL;
-
-                option_hex = hexmem(data, data_len);
-                if (!option_hex) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-                fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
-        }
-
-        LIST_FOREACH(options, option, lease->private_options) {
-                char key[STRLEN("OPTION_000")+1];
-
-                xsprintf(key, "OPTION_%" PRIu8, option->tag);
-                r = serialize_dhcp_option(f, key, option->data, option->length);
-                if (r < 0)
-                        goto fail;
-        }
-
-        r = fflush_and_check(f);
-        if (r < 0)
-                goto fail;
-
-        if (rename(temp_path, lease_file) < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        return 0;
-
-fail:
-        if (temp_path)
-                (void) unlink(temp_path);
-
-        return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
-}
-
-int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
-
-        _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
-        _cleanup_free_ char
-                *address = NULL,
-                *router = NULL,
-                *netmask = NULL,
-                *server_address = NULL,
-                *next_server = NULL,
-                *broadcast = NULL,
-                *dns = NULL,
-                *ntp = NULL,
-                *sip = NULL,
-                *pop3 = NULL,
-                *smtp = NULL,
-                *lpr = NULL,
-                *mtu = NULL,
-                *routes = NULL,
-                *domains = NULL,
-                *client_id_hex = NULL,
-                *vendor_specific_hex = NULL,
-                *lifetime = NULL,
-                *t1 = NULL,
-                *t2 = NULL,
-                *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {};
-
-        int r, i;
-
-        assert(lease_file);
-        assert(ret);
-
-        r = dhcp_lease_new(&lease);
-        if (r < 0)
-                return r;
-
-        r = parse_env_file(NULL, lease_file,
-                           "ADDRESS", &address,
-                           "ROUTER", &router,
-                           "NETMASK", &netmask,
-                           "SERVER_ADDRESS", &server_address,
-                           "NEXT_SERVER", &next_server,
-                           "BROADCAST", &broadcast,
-                           "DNS", &dns,
-                           "NTP", &ntp,
-                           "SIP", &sip,
-                           "POP3", &pop3,
-                           "SMTP", &smtp,
-                           "LPR", &lpr,
-                           "MTU", &mtu,
-                           "DOMAINNAME", &lease->domainname,
-                           "HOSTNAME", &lease->hostname,
-                           "DOMAIN_SEARCH_LIST", &domains,
-                           "ROOT_PATH", &lease->root_path,
-                           "ROUTES", &routes,
-                           "CLIENTID", &client_id_hex,
-                           "TIMEZONE", &lease->timezone,
-                           "VENDOR_SPECIFIC", &vendor_specific_hex,
-                           "LIFETIME", &lifetime,
-                           "T1", &t1,
-                           "T2", &t2,
-                           "OPTION_224", &options[0],
-                           "OPTION_225", &options[1],
-                           "OPTION_226", &options[2],
-                           "OPTION_227", &options[3],
-                           "OPTION_228", &options[4],
-                           "OPTION_229", &options[5],
-                           "OPTION_230", &options[6],
-                           "OPTION_231", &options[7],
-                           "OPTION_232", &options[8],
-                           "OPTION_233", &options[9],
-                           "OPTION_234", &options[10],
-                           "OPTION_235", &options[11],
-                           "OPTION_236", &options[12],
-                           "OPTION_237", &options[13],
-                           "OPTION_238", &options[14],
-                           "OPTION_239", &options[15],
-                           "OPTION_240", &options[16],
-                           "OPTION_241", &options[17],
-                           "OPTION_242", &options[18],
-                           "OPTION_243", &options[19],
-                           "OPTION_244", &options[20],
-                           "OPTION_245", &options[21],
-                           "OPTION_246", &options[22],
-                           "OPTION_247", &options[23],
-                           "OPTION_248", &options[24],
-                           "OPTION_249", &options[25],
-                           "OPTION_250", &options[26],
-                           "OPTION_251", &options[27],
-                           "OPTION_252", &options[28],
-                           "OPTION_253", &options[29],
-                           "OPTION_254", &options[30]);
-        if (r < 0)
-                return r;
-
-        if (address) {
-                r = inet_pton(AF_INET, address, &lease->address);
-                if (r <= 0)
-                        log_debug("Failed to parse address %s, ignoring.", address);
-        }
-
-        if (router) {
-                r = deserialize_in_addrs(&lease->router, router);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize router addresses %s, ignoring: %m", router);
-                else
-                        lease->router_size = r;
-        }
-
-        if (netmask) {
-                r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
-                if (r <= 0)
-                        log_debug("Failed to parse netmask %s, ignoring.", netmask);
-                else
-                        lease->have_subnet_mask = true;
-        }
-
-        if (server_address) {
-                r = inet_pton(AF_INET, server_address, &lease->server_address);
-                if (r <= 0)
-                        log_debug("Failed to parse server address %s, ignoring.", server_address);
-        }
-
-        if (next_server) {
-                r = inet_pton(AF_INET, next_server, &lease->next_server);
-                if (r <= 0)
-                        log_debug("Failed to parse next server %s, ignoring.", next_server);
-        }
-
-        if (broadcast) {
-                r = inet_pton(AF_INET, broadcast, &lease->broadcast);
-                if (r <= 0)
-                        log_debug("Failed to parse broadcast address %s, ignoring.", broadcast);
-                else
-                        lease->have_broadcast = true;
-        }
-
-        if (dns) {
-                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_DNS].addr, dns);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
-                else
-                        lease->servers[SD_DHCP_LEASE_DNS].size = r;
-        }
-
-        if (ntp) {
-                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
-                else
-                        lease->servers[SD_DHCP_LEASE_NTP].size = r;
-        }
-
-        if (sip) {
-                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SIP].addr, sip);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip);
-                else
-                        lease->servers[SD_DHCP_LEASE_SIP].size = r;
-        }
-
-        if (pop3) {
-                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_POP3].addr, pop3);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3);
-                else
-                        lease->servers[SD_DHCP_LEASE_POP3].size = r;
-        }
-
-        if (smtp) {
-                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SMTP].addr, smtp);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp);
-                else
-                        lease->servers[SD_DHCP_LEASE_SMTP].size = r;
-        }
-
-        if (lpr) {
-                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr);
-                else
-                        lease->servers[SD_DHCP_LEASE_LPR].size = r;
-        }
-
-        if (mtu) {
-                r = safe_atou16(mtu, &lease->mtu);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
-        }
-
-        if (domains) {
-                _cleanup_strv_free_ char **a = NULL;
-                a = strv_split(domains, " ");
-                if (!a)
-                        return -ENOMEM;
-
-                if (!strv_isempty(a))
-                        lease->search_domains = TAKE_PTR(a);
-        }
-
-        if (routes) {
-                r = deserialize_dhcp_routes(
-                                &lease->static_route,
-                                &lease->static_route_size,
-                                &lease->static_route_allocated,
-                                routes);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
-        }
-
-        if (lifetime) {
-                r = safe_atou32(lifetime, &lease->lifetime);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime);
-        }
-
-        if (t1) {
-                r = safe_atou32(t1, &lease->t1);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1);
-        }
-
-        if (t2) {
-                r = safe_atou32(t2, &lease->t2);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2);
-        }
-
-        if (client_id_hex) {
-                r = unhexmem(client_id_hex, (size_t) -1, &lease->client_id, &lease->client_id_len);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
-        }
-
-        if (vendor_specific_hex) {
-                r = unhexmem(vendor_specific_hex, (size_t) -1, &lease->vendor_specific, &lease->vendor_specific_len);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
-        }
-
-        for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) {
-                _cleanup_free_ void *data = NULL;
-                size_t len;
-
-                if (!options[i])
-                        continue;
-
-                r = unhexmem(options[i], (size_t) -1, &data, &len);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
-                        continue;
-                }
-
-                r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret = TAKE_PTR(lease);
-
-        return 0;
-}
-
-int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
-        struct in_addr address, mask;
-        int r;
-
-        assert(lease);
-
-        if (lease->address == 0)
-                return -ENODATA;
-
-        address.s_addr = lease->address;
-
-        /* fall back to the default subnet masks based on address class */
-        r = in4_addr_default_subnet_mask(&address, &mask);
-        if (r < 0)
-                return r;
-
-        lease->subnet_mask = mask.s_addr;
-        lease->have_subnet_mask = true;
-
-        return 0;
-}
-
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
-        assert_return(lease, -EINVAL);
-        assert_return(client_id, -EINVAL);
-        assert_return(client_id_len, -EINVAL);
-
-        if (!lease->client_id)
-                return -ENODATA;
-
-        *client_id = lease->client_id;
-        *client_id_len = lease->client_id_len;
-
-        return 0;
-}
-
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
-        assert_return(lease, -EINVAL);
-        assert_return(client_id || client_id_len <= 0, -EINVAL);
-
-        if (client_id_len <= 0)
-                lease->client_id = mfree(lease->client_id);
-        else {
-                void *p;
-
-                p = memdup(client_id, client_id_len);
-                if (!p)
-                        return -ENOMEM;
-
-                free(lease->client_id);
-                lease->client_id = p;
-                lease->client_id_len = client_id_len;
-        }
-
-        return 0;
-}
-
-int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
-        assert_return(lease, -EINVAL);
-        assert_return(tz, -EINVAL);
-
-        if (!lease->timezone)
-                return -ENODATA;
-
-        *tz = lease->timezone;
-        return 0;
-}
-
-int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
-        assert_return(route, -EINVAL);
-        assert_return(destination, -EINVAL);
-
-        *destination = route->dst_addr;
-        return 0;
-}
-
-int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
-        assert_return(route, -EINVAL);
-        assert_return(length, -EINVAL);
-
-        *length = route->dst_prefixlen;
-        return 0;
-}
-
-int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
-        assert_return(route, -EINVAL);
-        assert_return(gateway, -EINVAL);
-
-        *gateway = route->gw_addr;
-        return 0;
-}
-
-int sd_dhcp_route_get_option(sd_dhcp_route *route) {
-        assert_return(route, -EINVAL);
-
-        return route->option;
-}
diff --git a/src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c
deleted file mode 100644
index 3fafd3c0..00000000
--- a/src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ /dev/null
@@ -1,1857 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014-2015 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <sys/ioctl.h>
-#if 0 /* NM_IGNORED */
-#include <linux/if_arp.h>
-#else /* NM_IGNORED */
-#include <net/if_arp.h>
-#endif /* NM_IGNORED */
-#include <linux/if_infiniband.h>
-
-#include "sd-dhcp6-client.h"
-
-#include "alloc-util.h"
-#include "dhcp-identifier.h"
-#include "dhcp6-internal.h"
-#include "dhcp6-lease-internal.h"
-#include "dhcp6-protocol.h"
-#include "dns-domain.h"
-#include "event-util.h"
-#include "fd-util.h"
-#include "hexdecoct.h"
-#include "hostname-util.h"
-#include "in-addr-util.h"
-#include "random-util.h"
-#include "socket-util.h"
-#include "string-table.h"
-#include "strv.h"
-#include "util.h"
-#include "web-util.h"
-
-#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
-
-#define IRT_DEFAULT (1 * USEC_PER_DAY)
-#define IRT_MINIMUM (600 * USEC_PER_SEC)
-
-/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
-enum {
-        DHCP6_REQUEST_IA_NA                     = 1,
-        DHCP6_REQUEST_IA_TA                     = 2, /* currently not used */
-        DHCP6_REQUEST_IA_PD                     = 4,
-};
-
-struct sd_dhcp6_client {
-        unsigned n_ref;
-
-        enum DHCP6State state;
-        sd_event *event;
-        int event_priority;
-        int ifindex;
-        DHCP6Address hint_pd_prefix;
-        struct in6_addr local_address;
-        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
-        size_t mac_addr_len;
-        uint16_t arp_type;
-        DHCP6IA ia_na;
-        DHCP6IA ia_pd;
-        sd_event_source *timeout_t1;
-        sd_event_source *timeout_t2;
-        unsigned request;
-        be32_t transaction_id;
-        usec_t transaction_start;
-        struct sd_dhcp6_lease *lease;
-        int fd;
-        bool information_request;
-        bool iaid_set;
-        be16_t *req_opts;
-        size_t req_opts_allocated;
-        size_t req_opts_len;
-        char *fqdn;
-        char *mudurl;
-        char **user_class;
-        char **vendor_class;
-        sd_event_source *receive_message;
-        usec_t retransmit_time;
-        uint8_t retransmit_count;
-        sd_event_source *timeout_resend;
-        sd_event_source *timeout_resend_expire;
-        sd_dhcp6_client_callback_t callback;
-        void *userdata;
-        struct duid duid;
-        size_t duid_len;
-        usec_t information_request_time_usec;
-        usec_t information_refresh_time_usec;
-        OrderedHashmap *extra_options;
-        OrderedHashmap *vendor_options;
-};
-
-static const uint16_t default_req_opts[] = {
-        SD_DHCP6_OPTION_DNS_SERVERS,
-        SD_DHCP6_OPTION_DOMAIN_LIST,
-        SD_DHCP6_OPTION_NTP_SERVER,
-        SD_DHCP6_OPTION_SNTP_SERVERS,
-};
-
-const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
-        [DHCP6_SOLICIT] = "SOLICIT",
-        [DHCP6_ADVERTISE] = "ADVERTISE",
-        [DHCP6_REQUEST] = "REQUEST",
-        [DHCP6_CONFIRM] = "CONFIRM",
-        [DHCP6_RENEW] = "RENEW",
-        [DHCP6_REBIND] = "REBIND",
-        [DHCP6_REPLY] = "REPLY",
-        [DHCP6_RELEASE] = "RELEASE",
-        [DHCP6_DECLINE] = "DECLINE",
-        [DHCP6_RECONFIGURE] = "RECONFIGURE",
-        [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
-        [DHCP6_RELAY_FORW] = "RELAY-FORW",
-        [DHCP6_RELAY_REPL] = "RELAY-REPL",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
-
-const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
-        [DHCP6_STATUS_SUCCESS]                      = "Success",
-        [DHCP6_STATUS_UNSPEC_FAIL]                  = "Unspecified failure",
-        [DHCP6_STATUS_NO_ADDRS_AVAIL]               = "No addresses available",
-        [DHCP6_STATUS_NO_BINDING]                   = "Binding unavailable",
-        [DHCP6_STATUS_NOT_ON_LINK]                  = "Not on link",
-        [DHCP6_STATUS_USE_MULTICAST]                = "Use multicast",
-        [DHCP6_STATUS_NO_PREFIX_AVAIL]              = "No prefix available",
-        [DHCP6_STATUS_UNKNOWN_QUERY_TYPE]           = "Unknown query type",
-        [DHCP6_STATUS_MALFORMED_QUERY]              = "Malformed query",
-        [DHCP6_STATUS_NOT_CONFIGURED]               = "Not configured",
-        [DHCP6_STATUS_NOT_ALLOWED]                  = "Not allowed",
-        [DHCP6_STATUS_QUERY_TERMINATED]             = "Query terminated",
-        [DHCP6_STATUS_DATA_MISSING]                 = "Data missing",
-        [DHCP6_STATUS_CATCHUP_COMPLETE]             = "Catch up complete",
-        [DHCP6_STATUS_NOT_SUPPORTED]                = "Not supported",
-        [DHCP6_STATUS_TLS_CONNECTION_REFUSED]       = "TLS connection refused",
-        [DHCP6_STATUS_ADDRESS_IN_USE]               = "Address in use",
-        [DHCP6_STATUS_CONFIGURATION_CONFLICT]       = "Configuration conflict",
-        [DHCP6_STATUS_MISSING_BINDING_INFORMATION]  = "Missing binding information",
-        [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
-        [DHCP6_STATUS_SERVER_SHUTTING_DOWN]         = "Server shutting down",
-        [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED]     = "DNS update not supported",
-        [DHCP6_STATUS_EXCESSIVE_TIME_SKEW]          = "Excessive time skew",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
-
-#define DHCP6_CLIENT_DONT_DESTROY(client) \
-        _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
-
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
-
-int sd_dhcp6_client_set_callback(
-                sd_dhcp6_client *client,
-                sd_dhcp6_client_callback_t cb,
-                void *userdata) {
-
-        assert_return(client, -EINVAL);
-
-        client->callback = cb;
-        client->userdata = userdata;
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
-
-        assert_return(client, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
-
-        client->ifindex = ifindex;
-        return 0;
-}
-
-int sd_dhcp6_client_set_local_address(
-                sd_dhcp6_client *client,
-                const struct in6_addr *local_address) {
-
-        assert_return(client, -EINVAL);
-        assert_return(local_address, -EINVAL);
-        assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
-
-        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
-
-        client->local_address = *local_address;
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_mac(
-                sd_dhcp6_client *client,
-                const uint8_t *addr, size_t addr_len,
-                uint16_t arp_type) {
-
-        assert_return(client, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
-
-        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(addr_len == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
-        else {
-                client->arp_type = ARPHRD_NONE;
-                client->mac_addr_len = 0;
-                return 0;
-        }
-
-        if (client->mac_addr_len == addr_len &&
-            memcmp(&client->mac_addr, addr, addr_len) == 0)
-                return 0;
-
-        memcpy(&client->mac_addr, addr, addr_len);
-        client->mac_addr_len = addr_len;
-        client->arp_type = arp_type;
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_prefix_delegation_hint(
-                sd_dhcp6_client *client,
-                uint8_t prefixlen,
-                const struct in6_addr *pd_address) {
-
-        assert_return(client, -EINVAL);
-        assert_return(pd_address, -EINVAL);
-
-        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
-
-        client->hint_pd_prefix.iapdprefix.address = *pd_address;
-        client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
-
-        return 0;
-}
-
-int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(v, -EINVAL);
-
-        r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = ordered_hashmap_put(client->vendor_options, v, v);
-        if (r < 0)
-                return r;
-
-        sd_dhcp6_option_ref(v);
-
-        return 1;
-}
-
-static int client_ensure_duid(sd_dhcp6_client *client) {
-        if (client->duid_len != 0)
-                return 0;
-
-        return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
-}
-
-/**
- * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
- * without further modification. Otherwise, if duid_type is supported, DUID
- * is set based on that type. Otherwise, an error is returned.
- */
-static int dhcp6_client_set_duid_internal(
-                sd_dhcp6_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len,
-                usec_t llt_time) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(duid_len == 0 || duid != NULL, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
-
-        if (duid) {
-                r = dhcp_validate_duid_len(duid_type, duid_len, true);
-                if (r < 0) {
-                        r = dhcp_validate_duid_len(duid_type, duid_len, false);
-                        if (r < 0)
-                                return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
-
-                        log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
-                }
-
-                client->duid.type = htobe16(duid_type);
-                memcpy(&client->duid.raw.data, duid, duid_len);
-                client->duid_len = sizeof(client->duid.type) + duid_len;
-        } else
-#if 0 /* NM_IGNORED */
-                switch (duid_type) {
-                case DUID_TYPE_LLT:
-                        if (client->mac_addr_len == 0)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
-
-                        r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
-                        if (r < 0)
-                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
-                        break;
-                case DUID_TYPE_EN:
-                        r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
-                        if (r < 0)
-                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
-                        break;
-                case DUID_TYPE_LL:
-                        if (client->mac_addr_len == 0)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
-
-                        r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
-                        if (r < 0)
-                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
-                        break;
-                case DUID_TYPE_UUID:
-                        r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
-                        if (r < 0)
-                                return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
-                        break;
-                default:
-                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
-                }
-#else /* NM_IGNORED */
-                g_return_val_if_reached (-EINVAL);
-#endif /* NM_IGNORED */
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_duid(
-                sd_dhcp6_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len) {
-        return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
-}
-
-int sd_dhcp6_client_set_duid_llt(
-                sd_dhcp6_client *client,
-                usec_t llt_time) {
-        return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
-}
-
-static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = {
-        [DUID_TYPE_LLT]  = "DUID-LLT",
-        [DUID_TYPE_EN]   = "DUID-EN/Vendor",
-        [DUID_TYPE_LL]   = "DUID-LL",
-        [DUID_TYPE_UUID] = "UUID",
-};
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType);
-
-int sd_dhcp6_client_duid_as_string(
-                sd_dhcp6_client *client,
-                char **duid) {
-        _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
-        const char *v;
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->duid_len > 0, -ENODATA);
-
-        v = dhcp6_duid_type_to_string(be16toh(client->duid.type));
-        if (v) {
-                s = strdup(v);
-                if (!s)
-                        return -ENOMEM;
-        } else {
-                r = asprintf(&s, "%0x", client->duid.type);
-                if (r < 0)
-                        return -ENOMEM;
-        }
-
-        t = hexmem(&client->duid.raw.data, client->duid_len);
-        if (!t)
-                return -ENOMEM;
-
-        p = strjoin(s, ":", t);
-        if (!p)
-                return -ENOMEM;
-
-        *duid = TAKE_PTR(p);
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
-        assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
-
-        client->ia_na.ia_na.id = htobe32(iaid);
-        client->ia_pd.ia_pd.id = htobe32(iaid);
-        client->iaid_set = true;
-
-        return 0;
-}
-
-int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
-        assert_return(client, -EINVAL);
-        assert_return(iaid, -EINVAL);
-
-        if (!client->iaid_set)
-                return -ENODATA;
-
-        *iaid = be32toh(client->ia_na.ia_na.id);
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_fqdn(
-                sd_dhcp6_client *client,
-                const char *fqdn) {
-
-        assert_return(client, -EINVAL);
-
-        /* Make sure FQDN qualifies as DNS and as Linux hostname */
-        if (fqdn &&
-            !(hostname_is_valid(fqdn, 0) && dns_name_is_valid(fqdn) > 0))
-                return -EINVAL;
-
-        return free_and_strdup(&client->fqdn, fqdn);
-}
-
-int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
-        assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
-
-        client->information_request = enabled;
-
-        return 0;
-}
-
-int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
-        assert_return(client, -EINVAL);
-        assert_return(enabled, -EINVAL);
-
-        *enabled = client->information_request;
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
-        size_t t;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
-
-        if (option <= 0 || option >= UINT8_MAX)
-                return -EINVAL;
-
-        for (t = 0; t < client->req_opts_len; t++)
-                if (client->req_opts[t] == htobe16(option))
-                        return -EEXIST;
-
-        if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
-                            client->req_opts_len + 1))
-                return -ENOMEM;
-
-        client->req_opts[client->req_opts_len++] = htobe16(option);
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
-
-        assert_return(client, -EINVAL);
-        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
-        assert_return(mudurl, -EINVAL);
-        assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
-        assert_return(http_url_is_valid(mudurl), -EINVAL);
-
-        return free_and_strdup(&client->mudurl, mudurl);
-}
-
-int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) {
-        _cleanup_strv_free_ char **s = NULL;
-        char **p;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
-
-        assert_return(user_class, -EINVAL);
-
-        STRV_FOREACH(p, user_class)
-                if (strlen(*p) > UINT16_MAX)
-                        return -ENAMETOOLONG;
-
-        s = strv_copy(user_class);
-        if (!s)
-                return -ENOMEM;
-
-        client->user_class = TAKE_PTR(s);
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) {
-        _cleanup_strv_free_ char **s = NULL;
-        char **p;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
-        assert_return(vendor_class, -EINVAL);
-
-        STRV_FOREACH(p, vendor_class)
-                if (strlen(*p) > UINT8_MAX)
-                        return -ENAMETOOLONG;
-
-        s = strv_copy(vendor_class);
-        if (!s)
-                return -ENOMEM;
-
-        client->vendor_class = TAKE_PTR(s);
-
-        return 0;
-}
-
-int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
-        assert_return(client, -EINVAL);
-        assert_return(delegation, -EINVAL);
-
-        *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
-        assert_return(client, -EINVAL);
-
-        SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
-
-        return 0;
-}
-
-int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
-        assert_return(client, -EINVAL);
-        assert_return(request, -EINVAL);
-
-        *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
-        assert_return(client, -EINVAL);
-
-        SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
-
-        return 0;
-}
-
-int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
-        assert_return(client, -EINVAL);
-
-        client->transaction_id = transaction_id;
-
-        return 0;
-}
-
-int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
-        assert_return(client, -EINVAL);
-
-        if (!client->lease)
-                return -ENOMSG;
-
-        if (ret)
-                *ret = client->lease;
-
-        return 0;
-}
-
-int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(v, -EINVAL);
-
-        r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
-        if (r < 0)
-                return r;
-
-        sd_dhcp6_option_ref(v);
-        return 0;
-}
-
-static void client_notify(sd_dhcp6_client *client, int event) {
-        assert(client);
-
-        if (client->callback)
-                client->callback(client, event, client->userdata);
-}
-
-static int client_reset(sd_dhcp6_client *client) {
-        assert(client);
-
-        client->lease = sd_dhcp6_lease_unref(client->lease);
-
-        client->receive_message =
-                sd_event_source_unref(client->receive_message);
-
-        client->transaction_id = 0;
-        client->transaction_start = 0;
-
-        client->retransmit_time = 0;
-        client->retransmit_count = 0;
-
-        (void) event_source_disable(client->timeout_resend);
-        (void) event_source_disable(client->timeout_resend_expire);
-        (void) event_source_disable(client->timeout_t1);
-        (void) event_source_disable(client->timeout_t2);
-
-        client->state = DHCP6_STATE_STOPPED;
-
-        return 0;
-}
-
-static void client_stop(sd_dhcp6_client *client, int error) {
-        DHCP6_CLIENT_DONT_DESTROY(client);
-
-        assert(client);
-
-        client_notify(client, error);
-
-        client_reset(client);
-}
-
-static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
-        _cleanup_free_ DHCP6Message *message = NULL;
-        struct in6_addr all_servers =
-                IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
-        struct sd_dhcp6_option *j;
-        size_t len, optlen = 512;
-        uint8_t *opt;
-        int r;
-        usec_t elapsed_usec;
-        be16_t elapsed_time;
-
-        assert(client);
-
-        len = sizeof(DHCP6Message) + optlen;
-
-        message = malloc0(len);
-        if (!message)
-                return -ENOMEM;
-
-        opt = (uint8_t *)(message + 1);
-
-        message->transaction_id = client->transaction_id;
-
-        switch(client->state) {
-        case DHCP6_STATE_INFORMATION_REQUEST:
-                message->type = DHCP6_INFORMATION_REQUEST;
-
-                if (client->mudurl) {
-                        r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
-                                                client->mudurl);
-                        if (r < 0)
-                                return r;
-                }
-
-                break;
-
-        case DHCP6_STATE_SOLICITATION:
-                message->type = DHCP6_SOLICIT;
-
-                r = dhcp6_option_append(&opt, &optlen,
-                                        SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
-                if (r < 0)
-                        return r;
-
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
-                        r = dhcp6_option_append_ia(&opt, &optlen,
-                                                   &client->ia_na);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->fqdn) {
-                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->mudurl) {
-                        r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
-                                                client->mudurl);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->user_class) {
-                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->vendor_class) {
-                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (!ordered_hashmap_isempty(client->vendor_options)) {
-                        r = dhcp6_option_append_vendor_option(&opt, &optlen,
-                                                       client->vendor_options);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
-                        if (r < 0)
-                                return r;
-
-                        opt += r;
-                        optlen -= r;
-                }
-
-                break;
-
-        case DHCP6_STATE_REQUEST:
-        case DHCP6_STATE_RENEW:
-
-                if (client->state == DHCP6_STATE_REQUEST)
-                        message->type = DHCP6_REQUEST;
-                else
-                        message->type = DHCP6_RENEW;
-
-                r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
-                                        client->lease->serverid_len,
-                                        client->lease->serverid);
-                if (r < 0)
-                        return r;
-
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
-                        r = dhcp6_option_append_ia(&opt, &optlen,
-                                                   &client->lease->ia);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->fqdn) {
-                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->mudurl) {
-                        r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
-                                                client->mudurl);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->user_class) {
-                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->vendor_class) {
-                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (!ordered_hashmap_isempty(client->vendor_options)) {
-                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
-                        if (r < 0)
-                                return r;
-
-                        opt += r;
-                        optlen -= r;
-                }
-
-                break;
-
-        case DHCP6_STATE_REBIND:
-                message->type = DHCP6_REBIND;
-
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
-                        r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->fqdn) {
-                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->mudurl) {
-                        r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
-                                                client->mudurl);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->user_class) {
-                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->vendor_class) {
-                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (!ordered_hashmap_isempty(client->vendor_options)) {
-                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
-                        if (r < 0)
-                                return r;
-
-                        opt += r;
-                        optlen -= r;
-                }
-
-                break;
-
-        case DHCP6_STATE_STOPPED:
-        case DHCP6_STATE_BOUND:
-                return -EINVAL;
-        }
-
-        r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
-                                client->req_opts_len * sizeof(be16_t),
-                                client->req_opts);
-        if (r < 0)
-                return r;
-
-        assert(client->duid_len);
-        r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
-                                client->duid_len, &client->duid);
-        if (r < 0)
-                return r;
-
-        elapsed_usec = time_now - client->transaction_start;
-        if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
-                elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
-        else
-                elapsed_time = 0xffff;
-
-        r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
-                                sizeof(elapsed_time), &elapsed_time);
-        if (r < 0)
-                return r;
-
-        ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
-                r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
-                if (r < 0)
-                        return r;
-        }
-
-        r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
-                                          len - optlen);
-        if (r < 0)
-                return r;
-
-        log_dhcp6_client(client, "Sent %s",
-                         dhcp6_message_type_to_string(message->type));
-
-        return 0;
-}
-
-static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_dhcp6_client *client = userdata;
-
-        assert(s);
-        assert(client);
-        assert(client->lease);
-
-        (void) event_source_disable(client->timeout_t2);
-
-        log_dhcp6_client(client, "Timeout T2");
-
-        client_start(client, DHCP6_STATE_REBIND);
-
-        return 0;
-}
-
-static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_dhcp6_client *client = userdata;
-
-        assert(s);
-        assert(client);
-        assert(client->lease);
-
-        (void) event_source_disable(client->timeout_t1);
-
-        log_dhcp6_client(client, "Timeout T1");
-
-        client_start(client, DHCP6_STATE_RENEW);
-
-        return 0;
-}
-
-static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_dhcp6_client *client = userdata;
-        DHCP6_CLIENT_DONT_DESTROY(client);
-        enum DHCP6State state;
-
-        assert(s);
-        assert(client);
-        assert(client->event);
-
-        state = client->state;
-
-        client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
-
-        /* RFC 3315, section 18.1.4., says that "...the client may choose to
-           use a Solicit message to locate a new DHCP server..." */
-        if (state == DHCP6_STATE_REBIND)
-                client_start(client, DHCP6_STATE_SOLICITATION);
-
-        return 0;
-}
-
-static usec_t client_timeout_compute_random(usec_t val) {
-        return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC;
-}
-
-static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
-        int r = 0;
-        sd_dhcp6_client *client = userdata;
-        usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
-        usec_t max_retransmit_duration = 0;
-        uint8_t max_retransmit_count = 0;
-        char time_string[FORMAT_TIMESPAN_MAX];
-
-        assert(s);
-        assert(client);
-        assert(client->event);
-
-        (void) event_source_disable(client->timeout_resend);
-
-        switch (client->state) {
-        case DHCP6_STATE_INFORMATION_REQUEST:
-                init_retransmit_time = DHCP6_INF_TIMEOUT;
-                max_retransmit_time = DHCP6_INF_MAX_RT;
-
-                break;
-
-        case DHCP6_STATE_SOLICITATION:
-
-                if (client->retransmit_count && client->lease) {
-                        client_start(client, DHCP6_STATE_REQUEST);
-                        return 0;
-                }
-
-                init_retransmit_time = DHCP6_SOL_TIMEOUT;
-                max_retransmit_time = DHCP6_SOL_MAX_RT;
-
-                break;
-
-        case DHCP6_STATE_REQUEST:
-                init_retransmit_time = DHCP6_REQ_TIMEOUT;
-                max_retransmit_time = DHCP6_REQ_MAX_RT;
-                max_retransmit_count = DHCP6_REQ_MAX_RC;
-
-                break;
-
-        case DHCP6_STATE_RENEW:
-                init_retransmit_time = DHCP6_REN_TIMEOUT;
-                max_retransmit_time = DHCP6_REN_MAX_RT;
-
-                /* RFC 3315, section 18.1.3. says max retransmit duration will
-                   be the remaining time until T2. Instead of setting MRD,
-                   wait for T2 to trigger with the same end result */
-
-                break;
-
-        case DHCP6_STATE_REBIND:
-                init_retransmit_time = DHCP6_REB_TIMEOUT;
-                max_retransmit_time = DHCP6_REB_MAX_RT;
-
-                if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
-                        uint32_t expire = 0;
-
-                        r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire);
-                        if (r < 0) {
-                                client_stop(client, r);
-                                return 0;
-                        }
-                        max_retransmit_duration = expire * USEC_PER_SEC;
-                }
-
-                break;
-
-        case DHCP6_STATE_STOPPED:
-        case DHCP6_STATE_BOUND:
-                return 0;
-        }
-
-        if (max_retransmit_count > 0 &&
-            client->retransmit_count >= max_retransmit_count) {
-                client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
-                return 0;
-        }
-
-        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0)
-                goto error;
-
-        r = client_send_message(client, time_now);
-        if (r >= 0)
-                client->retransmit_count++;
-
-        if (client->retransmit_time == 0) {
-                client->retransmit_time =
-                        client_timeout_compute_random(init_retransmit_time);
-
-                if (client->state == DHCP6_STATE_SOLICITATION)
-                        client->retransmit_time += init_retransmit_time / 10;
-
-        } else {
-                if (max_retransmit_time > 0 &&
-                    client->retransmit_time > max_retransmit_time / 2)
-                        client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
-                else
-                        client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
-        }
-
-        log_dhcp6_client(client, "Next retransmission in %s",
-                         format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
-
-        r = event_reset_time(client->event, &client->timeout_resend,
-                             clock_boottime_or_monotonic(),
-                             time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
-                             client_timeout_resend, client,
-                             client->event_priority, "dhcp6-resend-timer", true);
-        if (r < 0)
-                goto error;
-
-        if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
-
-                log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
-                                 max_retransmit_duration / USEC_PER_SEC);
-
-                r = event_reset_time(client->event, &client->timeout_resend_expire,
-                                     clock_boottime_or_monotonic(),
-                                     time_now + max_retransmit_duration, USEC_PER_SEC,
-                                     client_timeout_resend_expire, client,
-                                     client->event_priority, "dhcp6-resend-expire-timer", true);
-                if (r < 0)
-                        goto error;
-        }
-
-error:
-        if (r < 0)
-                client_stop(client, r);
-
-        return 0;
-}
-
-static int client_ensure_iaid(sd_dhcp6_client *client) {
-        int r;
-        uint32_t iaid;
-
-        assert(client);
-
-        if (client->iaid_set)
-                return 0;
-
-        r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
-        if (r < 0)
-                return r;
-
-        client->ia_na.ia_na.id = iaid;
-        client->ia_pd.ia_pd.id = iaid;
-        client->iaid_set = true;
-
-        return 0;
-}
-
-static int client_parse_message(
-                sd_dhcp6_client *client,
-                DHCP6Message *message,
-                size_t len,
-                sd_dhcp6_lease *lease) {
-
-        uint16_t ia_na_status = 0, ia_pd_status = 0;
-        uint32_t lt_t1 = ~0, lt_t2 = ~0;
-        usec_t irt = IRT_DEFAULT;
-        bool clientid = false;
-        size_t pos = 0;
-        int r;
-
-        assert(client);
-        assert(message);
-        assert(len >= sizeof(DHCP6Message));
-        assert(lease);
-
-        len -= sizeof(DHCP6Message);
-
-        while (pos < len) {
-                DHCP6Option *option = (DHCP6Option *) &message->options[pos];
-                uint16_t optcode, optlen;
-                be32_t iaid_lease;
-                int  status;
-                uint8_t *optval;
-
-                if (len < pos + offsetof(DHCP6Option, data))
-                        return -ENOBUFS;
-
-                optcode = be16toh(option->code);
-                optlen = be16toh(option->len);
-                optval = option->data;
-
-                if (len < pos + offsetof(DHCP6Option, data) + optlen)
-                        return -ENOBUFS;
-
-                switch (optcode) {
-                case SD_DHCP6_OPTION_CLIENTID:
-                        if (clientid) {
-                                log_dhcp6_client(client, "%s contains multiple clientids",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
-
-                        if (optlen != client->duid_len ||
-                            memcmp(&client->duid, optval, optlen) != 0) {
-                                log_dhcp6_client(client, "%s DUID does not match",
-                                                 dhcp6_message_type_to_string(message->type));
-
-                                return -EINVAL;
-                        }
-                        clientid = true;
-
-                        break;
-
-                case SD_DHCP6_OPTION_SERVERID:
-                        r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                        if (r >= 0) {
-                                log_dhcp6_client(client, "%s contains multiple serverids",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
-
-                        r = dhcp6_lease_set_serverid(lease, optval, optlen);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_PREFERENCE:
-                        if (optlen != 1)
-                                return -EINVAL;
-
-                        r = dhcp6_lease_set_preference(lease, optval[0]);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_STATUS_CODE:
-                        status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
-                        if (status < 0)
-                                return status;
-
-                        if (status > 0) {
-                                log_dhcp6_client(client, "%s Status %s",
-                                                 dhcp6_message_type_to_string(message->type),
-                                                 dhcp6_message_status_to_string(status));
-
-                                return -EINVAL;
-                        }
-
-                        break;
-
-                case SD_DHCP6_OPTION_IA_NA:
-                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                                log_dhcp6_client(client, "Information request ignoring IA NA option");
-
-                                break;
-                        }
-
-                        r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
-                        if (r < 0 && r != -ENOMSG)
-                                return r;
-
-                        if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
-                                pos += offsetof(DHCP6Option, data) + optlen;
-                                continue;
-                        }
-
-                        r = dhcp6_lease_get_iaid(lease, &iaid_lease);
-                        if (r < 0)
-                                return r;
-
-                        if (client->ia_na.ia_na.id != iaid_lease) {
-                                log_dhcp6_client(client, "%s has wrong IAID for IA NA",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
-
-                        if (lease->ia.addresses) {
-                                lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
-                                lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
-                        }
-
-                        break;
-
-                case SD_DHCP6_OPTION_IA_PD:
-                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                                log_dhcp6_client(client, "Information request ignoring IA PD option");
-
-                                break;
-                        }
-
-                        r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
-                        if (r < 0 && r != -ENOMSG)
-                                return r;
-
-                        if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
-                                pos += offsetof(DHCP6Option, data) + optlen;
-                                continue;
-                        }
-
-                        r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
-                        if (r < 0)
-                                return r;
-
-                        if (client->ia_pd.ia_pd.id != iaid_lease) {
-                                log_dhcp6_client(client, "%s has wrong IAID for IA PD",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
-
-                        if (lease->pd.addresses) {
-                                lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
-                                lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
-                        }
-
-                        break;
-
-                case SD_DHCP6_OPTION_RAPID_COMMIT:
-                        r = dhcp6_lease_set_rapid_commit(lease);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_DNS_SERVERS:
-                        r = dhcp6_lease_set_dns(lease, optval, optlen);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_DOMAIN_LIST:
-                        r = dhcp6_lease_set_domains(lease, optval, optlen);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_NTP_SERVER:
-                        r = dhcp6_lease_set_ntp(lease, optval, optlen);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_SNTP_SERVERS:
-                        r = dhcp6_lease_set_sntp(lease, optval, optlen);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_FQDN:
-                        r = dhcp6_lease_set_fqdn(lease, optval, optlen);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
-                        if (optlen != 4)
-                                return -EINVAL;
-
-                        irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
-                        break;
-                }
-
-                pos += offsetof(DHCP6Option, data) + optlen;
-        }
-
-        if (ia_na_status > 0 && ia_pd_status > 0) {
-                log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
-                return -EINVAL;
-        }
-
-        if (!clientid) {
-                log_dhcp6_client(client, "%s has incomplete options",
-                                 dhcp6_message_type_to_string(message->type));
-                return -EINVAL;
-        }
-
-        if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
-                r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                if (r < 0) {
-                        log_dhcp6_client(client, "%s has no server id",
-                                         dhcp6_message_type_to_string(message->type));
-                        return -EINVAL;
-                }
-
-        } else {
-                if (lease->ia.addresses) {
-                        lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
-                        lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
-                }
-
-                if (lease->pd.addresses) {
-                        lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
-                        lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
-                }
-        }
-
-        client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
-
-        return 0;
-}
-
-static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
-        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
-        bool rapid_commit;
-        int r;
-
-        assert(client);
-        assert(reply);
-
-        if (reply->type != DHCP6_REPLY)
-                return 0;
-
-        r = dhcp6_lease_new(&lease);
-        if (r < 0)
-                return -ENOMEM;
-
-        r = client_parse_message(client, reply, len, lease);
-        if (r < 0)
-                return r;
-
-        if (client->state == DHCP6_STATE_SOLICITATION) {
-                r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
-                if (r < 0)
-                        return r;
-
-                if (!rapid_commit)
-                        return 0;
-        }
-
-        sd_dhcp6_lease_unref(client->lease);
-        client->lease = TAKE_PTR(lease);
-
-        return DHCP6_STATE_BOUND;
-}
-
-static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
-        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
-        uint8_t pref_advertise = 0, pref_lease = 0;
-        int r;
-
-        if (advertise->type != DHCP6_ADVERTISE)
-                return 0;
-
-        r = dhcp6_lease_new(&lease);
-        if (r < 0)
-                return r;
-
-        r = client_parse_message(client, advertise, len, lease);
-        if (r < 0)
-                return r;
-
-        r = dhcp6_lease_get_preference(lease, &pref_advertise);
-        if (r < 0)
-                return r;
-
-        r = dhcp6_lease_get_preference(client->lease, &pref_lease);
-
-        if (r < 0 || pref_advertise > pref_lease) {
-                sd_dhcp6_lease_unref(client->lease);
-                client->lease = TAKE_PTR(lease);
-                r = 0;
-        }
-
-        if (pref_advertise == 255 || client->retransmit_count > 1)
-                r = DHCP6_STATE_REQUEST;
-
-        return r;
-}
-
-static int client_receive_message(
-                sd_event_source *s,
-                int fd, uint32_t
-                revents,
-                void *userdata) {
-
-        sd_dhcp6_client *client = userdata;
-        DHCP6_CLIENT_DONT_DESTROY(client);
-        _cleanup_free_ DHCP6Message *message = NULL;
-        ssize_t buflen, len;
-        int r = 0;
-
-        assert(s);
-        assert(client);
-        assert(client->event);
-
-        buflen = next_datagram_size_fd(fd);
-        if (buflen == -ENETDOWN)
-                /* the link is down. Don't return an error or the I/O event
-                   source will be disconnected and we won't be able to receive
-                   packets again when the link comes back. */
-                return 0;
-        if (buflen < 0)
-                return buflen;
-
-        message = malloc(buflen);
-        if (!message)
-                return -ENOMEM;
-
-        len = recv(fd, message, buflen, 0);
-        if (len < 0) {
-                /* see comment above for why we shouldn't error out on ENETDOWN. */
-                if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
-                        return 0;
-
-                return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
-
-        }
-        if ((size_t) len < sizeof(DHCP6Message)) {
-                log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
-                return 0;
-        }
-
-        switch(message->type) {
-        case DHCP6_SOLICIT:
-        case DHCP6_REQUEST:
-        case DHCP6_CONFIRM:
-        case DHCP6_RENEW:
-        case DHCP6_REBIND:
-        case DHCP6_RELEASE:
-        case DHCP6_DECLINE:
-        case DHCP6_INFORMATION_REQUEST:
-        case DHCP6_RELAY_FORW:
-        case DHCP6_RELAY_REPL:
-                return 0;
-
-        case DHCP6_ADVERTISE:
-        case DHCP6_REPLY:
-        case DHCP6_RECONFIGURE:
-                break;
-
-        default:
-                log_dhcp6_client(client, "Unknown message type %d", message->type);
-                return 0;
-        }
-
-        if (client->transaction_id != (message->transaction_id &
-                                       htobe32(0x00ffffff)))
-                return 0;
-
-        switch (client->state) {
-        case DHCP6_STATE_INFORMATION_REQUEST:
-                r = client_receive_reply(client, message, len);
-                if (r < 0)
-                        return 0;
-
-                client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
-
-                client_start(client, DHCP6_STATE_STOPPED);
-
-                break;
-
-        case DHCP6_STATE_SOLICITATION:
-                r = client_receive_advertise(client, message, len);
-
-                if (r == DHCP6_STATE_REQUEST) {
-                        client_start(client, r);
-
-                        break;
-                }
-
-                _fallthrough_; /* for Solicitation Rapid Commit option check */
-        case DHCP6_STATE_REQUEST:
-        case DHCP6_STATE_RENEW:
-        case DHCP6_STATE_REBIND:
-
-                r = client_receive_reply(client, message, len);
-                if (r < 0)
-                        return 0;
-
-                if (r == DHCP6_STATE_BOUND) {
-
-                        r = client_start(client, DHCP6_STATE_BOUND);
-                        if (r < 0) {
-                                client_stop(client, r);
-                                return 0;
-                        }
-
-                        client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
-                }
-
-                break;
-
-        case DHCP6_STATE_BOUND:
-
-                break;
-
-        case DHCP6_STATE_STOPPED:
-                return 0;
-        }
-
-        log_dhcp6_client(client, "Recv %s",
-                         dhcp6_message_type_to_string(message->type));
-
-        return 0;
-}
-
-static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
-                               uint32_t *lifetime_t2) {
-        assert_return(client, -EINVAL);
-        assert_return(client->lease, -EINVAL);
-
-        if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
-                *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
-                *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
-
-                return 0;
-        }
-
-        if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
-                *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
-                *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
-
-                return 0;
-        }
-
-        return -ENOMSG;
-}
-
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
-        int r;
-        usec_t timeout, time_now;
-        char time_string[FORMAT_TIMESPAN_MAX];
-        uint32_t lifetime_t1, lifetime_t2;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->event, -EINVAL);
-        assert_return(client->ifindex > 0, -EINVAL);
-        assert_return(client->state != state, -EINVAL);
-
-        (void) event_source_disable(client->timeout_resend_expire);
-        (void) event_source_disable(client->timeout_resend);
-        client->retransmit_time = 0;
-        client->retransmit_count = 0;
-
-        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0)
-                return r;
-
-        if (!client->receive_message) {
-                r = sd_event_add_io(client->event, &client->receive_message,
-                                    client->fd, EPOLLIN, client_receive_message,
-                                    client);
-                if (r < 0)
-                        goto error;
-
-                r = sd_event_source_set_priority(client->receive_message,
-                                                 client->event_priority);
-                if (r < 0)
-                        goto error;
-
-                r = sd_event_source_set_description(client->receive_message,
-                                                    "dhcp6-receive-message");
-                if (r < 0)
-                        goto error;
-        }
-
-        switch (state) {
-        case DHCP6_STATE_STOPPED:
-                if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                        client->state = DHCP6_STATE_STOPPED;
-
-                        return 0;
-                }
-
-                _fallthrough_;
-        case DHCP6_STATE_SOLICITATION:
-                client->state = DHCP6_STATE_SOLICITATION;
-
-                break;
-
-        case DHCP6_STATE_INFORMATION_REQUEST:
-        case DHCP6_STATE_REQUEST:
-        case DHCP6_STATE_RENEW:
-        case DHCP6_STATE_REBIND:
-
-                client->state = state;
-
-                break;
-
-        case DHCP6_STATE_BOUND:
-
-                r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
-                if (r < 0)
-                        goto error;
-
-                if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
-                        log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
-                                         lifetime_t1, lifetime_t2);
-
-                        return 0;
-                }
-
-                timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
-
-                log_dhcp6_client(client, "T1 expires in %s",
-                                 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
-
-                r = event_reset_time(client->event, &client->timeout_t1,
-                                     clock_boottime_or_monotonic(),
-                                     time_now + timeout, 10 * USEC_PER_SEC,
-                                     client_timeout_t1, client,
-                                     client->event_priority, "dhcp6-t1-timeout", true);
-                if (r < 0)
-                        goto error;
-
-                timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
-
-                log_dhcp6_client(client, "T2 expires in %s",
-                                 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
-
-                r = event_reset_time(client->event, &client->timeout_t2,
-                                     clock_boottime_or_monotonic(),
-                                     time_now + timeout, 10 * USEC_PER_SEC,
-                                     client_timeout_t2, client,
-                                     client->event_priority, "dhcp6-t2-timeout", true);
-                if (r < 0)
-                        goto error;
-
-                client->state = state;
-
-                return 0;
-        }
-
-        client->transaction_id = random_u32() & htobe32(0x00ffffff);
-        client->transaction_start = time_now;
-
-        r = event_reset_time(client->event, &client->timeout_resend,
-                             clock_boottime_or_monotonic(),
-                             0, 0,
-                             client_timeout_resend, client,
-                             client->event_priority, "dhcp6-resend-timeout", true);
-        if (r < 0)
-                goto error;
-
-        return 0;
-
- error:
-        client_reset(client);
-        return r;
-}
-
-int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
-        if (!client)
-                return 0;
-
-        client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
-
-        client->fd = safe_close(client->fd);
-
-        return 0;
-}
-
-int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
-        assert_return(client, -EINVAL);
-
-        return client->state != DHCP6_STATE_STOPPED;
-}
-
-int sd_dhcp6_client_start(sd_dhcp6_client *client) {
-        enum DHCP6State state = DHCP6_STATE_SOLICITATION;
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->event, -EINVAL);
-        assert_return(client->ifindex > 0, -EINVAL);
-        assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
-
-        if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
-                return -EBUSY;
-
-        if (!client->information_request && !client->request)
-                return -EINVAL;
-
-        r = client_reset(client);
-        if (r < 0)
-                return r;
-
-        r = client_ensure_iaid(client);
-        if (r < 0)
-                return r;
-
-        r = client_ensure_duid(client);
-        if (r < 0)
-                return r;
-
-        if (client->fd < 0) {
-                r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
-                if (r < 0) {
-                        _cleanup_free_ char *p = NULL;
-
-                        (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
-                        return log_dhcp6_client_errno(client, r,
-                                                      "Failed to bind to UDP socket at address %s: %m", strna(p));
-                }
-
-                client->fd = r;
-        }
-
-        if (client->information_request) {
-                usec_t t = now(CLOCK_MONOTONIC);
-
-                if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
-                        return 0;
-
-                client->information_request_time_usec = t;
-                state = DHCP6_STATE_INFORMATION_REQUEST;
-        }
-
-        log_dhcp6_client(client, "Started in %s mode",
-                         client->information_request ? "Information request" : "Managed");
-
-        return client_start(client, state);
-}
-
-int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
-        int r;
-
-        assert_return(client, -EINVAL);
-        assert_return(!client->event, -EBUSY);
-
-        if (event)
-                client->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&client->event);
-                if (r < 0)
-                        return 0;
-        }
-
-        client->event_priority = priority;
-
-        return 0;
-}
-
-int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
-        assert_return(client, -EINVAL);
-
-        client->event = sd_event_unref(client->event);
-
-        return 0;
-}
-
-sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
-        assert_return(client, NULL);
-
-        return client->event;
-}
-
-static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
-        assert(client);
-
-        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-        client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
-        client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
-        client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
-
-        client_reset(client);
-
-        client->fd = safe_close(client->fd);
-
-        sd_dhcp6_client_detach_event(client);
-
-        free(client->req_opts);
-        free(client->fqdn);
-        free(client->mudurl);
-
-        ordered_hashmap_free(client->extra_options);
-        strv_free(client->user_class);
-        strv_free(client->vendor_class);
-
-        return mfree(client);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
-
-int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
-        _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
-        _cleanup_free_ be16_t *req_opts = NULL;
-        size_t t;
-
-        assert_return(ret, -EINVAL);
-
-        req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
-        if (!req_opts)
-                return -ENOMEM;
-
-        for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
-                req_opts[t] = htobe16(default_req_opts[t]);
-
-        client = new(sd_dhcp6_client, 1);
-        if (!client)
-                return -ENOMEM;
-
-        *client = (sd_dhcp6_client) {
-                .n_ref = 1,
-                .ia_na.type = SD_DHCP6_OPTION_IA_NA,
-                .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
-                .ifindex = -1,
-                .request = DHCP6_REQUEST_IA_NA,
-                .fd = -1,
-                .req_opts_len = ELEMENTSOF(default_req_opts),
-                .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
-                .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1,
-                .req_opts = TAKE_PTR(req_opts),
-        };
-
-        *ret = TAKE_PTR(client);
-
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/core/systemd/src/libsystemd-network/sd-dhcp6-lease.c
deleted file mode 100644
index 5792f688..00000000
--- a/src/core/systemd/src/libsystemd-network/sd-dhcp6-lease.c
+++ /dev/null
@@ -1,435 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014-2015 Intel Corporation. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-
-#include "alloc-util.h"
-#include "dhcp6-lease-internal.h"
-#include "dhcp6-protocol.h"
-#include "strv.h"
-#include "util.h"
-
-int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
-        DHCP6Address *addr;
-        uint32_t valid = 0, t;
-
-        assert_return(ia, -EINVAL);
-        assert_return(expire, -EINVAL);
-
-        LIST_FOREACH(addresses, addr, ia->addresses) {
-                t = be32toh(addr->iaaddr.lifetime_valid);
-                if (valid < t)
-                        valid = t;
-        }
-
-        t = be32toh(ia->ia_na.lifetime_t2);
-        if (t > valid)
-                return -EINVAL;
-
-        *expire = valid - t;
-
-        return 0;
-}
-
-DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
-        DHCP6Address *address;
-
-        if (!ia)
-                return NULL;
-
-        while (ia->addresses) {
-                address = ia->addresses;
-
-                LIST_REMOVE(addresses, ia->addresses, address);
-
-                free(address);
-        }
-
-        return NULL;
-}
-
-int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
-                             size_t len) {
-        uint8_t *serverid;
-
-        assert_return(lease, -EINVAL);
-        assert_return(id, -EINVAL);
-
-        serverid = memdup(id, len);
-        if (!serverid)
-                return -ENOMEM;
-
-        free_and_replace(lease->serverid, serverid);
-        lease->serverid_len = len;
-
-        return 0;
-}
-
-int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) {
-        assert_return(lease, -EINVAL);
-
-        if (!lease->serverid)
-                return -ENOMSG;
-
-        if (id)
-                *id = lease->serverid;
-        if (len)
-                *len = lease->serverid_len;
-
-        return 0;
-}
-
-int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
-        assert_return(lease, -EINVAL);
-
-        lease->preference = preference;
-
-        return 0;
-}
-
-int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
-        assert_return(preference, -EINVAL);
-
-        if (!lease)
-                return -EINVAL;
-
-        *preference = lease->preference;
-
-        return 0;
-}
-
-int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
-        assert_return(lease, -EINVAL);
-
-        lease->rapid_commit = true;
-
-        return 0;
-}
-
-int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
-        assert_return(lease, -EINVAL);
-        assert_return(rapid_commit, -EINVAL);
-
-        *rapid_commit = lease->rapid_commit;
-
-        return 0;
-}
-
-int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
-        assert_return(lease, -EINVAL);
-        assert_return(iaid, -EINVAL);
-
-        *iaid = lease->ia.ia_na.id;
-
-        return 0;
-}
-
-int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
-        assert_return(lease, -EINVAL);
-        assert_return(iaid, -EINVAL);
-
-        *iaid = lease->pd.ia_pd.id;
-
-        return 0;
-}
-
-int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
-                               uint32_t *lifetime_preferred,
-                               uint32_t *lifetime_valid) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(lifetime_preferred, -EINVAL);
-        assert_return(lifetime_valid, -EINVAL);
-
-        if (!lease->addr_iter)
-                return -ENOMSG;
-
-        memcpy(addr, &lease->addr_iter->iaaddr.address,
-                sizeof(struct in6_addr));
-        *lifetime_preferred =
-                be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
-        *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
-
-        lease->addr_iter = lease->addr_iter->addresses_next;
-
-        return 0;
-}
-
-void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
-        if (lease)
-                lease->addr_iter = lease->ia.addresses;
-}
-
-int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
-                          uint8_t *prefix_len,
-                          uint32_t *lifetime_preferred,
-                          uint32_t *lifetime_valid) {
-        assert_return(lease, -EINVAL);
-        assert_return(prefix, -EINVAL);
-        assert_return(prefix_len, -EINVAL);
-        assert_return(lifetime_preferred, -EINVAL);
-        assert_return(lifetime_valid, -EINVAL);
-
-        if (!lease->prefix_iter)
-                return -ENOMSG;
-
-        memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
-               sizeof(struct in6_addr));
-        *prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
-        *lifetime_preferred =
-                be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
-        *lifetime_valid =
-                be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
-
-        lease->prefix_iter = lease->prefix_iter->addresses_next;
-
-        return 0;
-}
-
-void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
-        if (lease)
-                lease->prefix_iter = lease->pd.addresses;
-}
-
-int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
-        int r;
-
-        assert_return(lease, -EINVAL);
-        assert_return(optval, -EINVAL);
-
-        if (!optlen)
-                return 0;
-
-        r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
-                                        lease->dns_count,
-                                        &lease->dns_allocated);
-        if (r < 0)
-                return log_dhcp6_client_errno(client, r, "Invalid DNS server option: %m");
-
-        lease->dns_count = r;
-
-        return 0;
-}
-
-int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) {
-        assert_return(lease, -EINVAL);
-        assert_return(addrs, -EINVAL);
-
-        if (lease->dns_count) {
-                *addrs = lease->dns;
-                return lease->dns_count;
-        }
-
-        return -ENOENT;
-}
-
-int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
-                            size_t optlen) {
-        int r;
-        char **domains;
-
-        assert_return(lease, -EINVAL);
-        assert_return(optval, -EINVAL);
-
-        if (!optlen)
-                return 0;
-
-        r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
-        if (r < 0)
-                return 0;
-
-        strv_free_and_replace(lease->domains, domains);
-        lease->domains_count = r;
-
-        return r;
-}
-
-int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) {
-        assert_return(lease, -EINVAL);
-        assert_return(domains, -EINVAL);
-
-        if (lease->domains_count) {
-                *domains = lease->domains;
-                return lease->domains_count;
-        }
-
-        return -ENOENT;
-}
-
-int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
-        int r;
-        uint16_t subopt;
-        size_t sublen;
-        uint8_t *subval;
-
-        assert_return(lease, -EINVAL);
-        assert_return(optval, -EINVAL);
-
-        lease->ntp = mfree(lease->ntp);
-        lease->ntp_count = 0;
-        lease->ntp_allocated = 0;
-
-        while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
-                                       &subval)) >= 0) {
-                int s;
-                char **servers;
-
-                switch(subopt) {
-                case DHCP6_NTP_SUBOPTION_SRV_ADDR:
-                case DHCP6_NTP_SUBOPTION_MC_ADDR:
-                        if (sublen != 16)
-                                return 0;
-
-                        s = dhcp6_option_parse_ip6addrs(subval, sublen,
-                                                        &lease->ntp,
-                                                        lease->ntp_count,
-                                                        &lease->ntp_allocated);
-                        if (s < 0)
-                                return s;
-
-                        lease->ntp_count = s;
-
-                        break;
-
-                case DHCP6_NTP_SUBOPTION_SRV_FQDN:
-                        r = dhcp6_option_parse_domainname_list(subval, sublen,
-                                                               &servers);
-                        if (r < 0)
-                                return 0;
-
-                        strv_free_and_replace(lease->ntp_fqdn, servers);
-                        lease->ntp_fqdn_count = r;
-
-                        break;
-                }
-        }
-
-        if (r != -ENOMSG)
-                return r;
-
-        return 0;
-}
-
-int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
-        int r;
-
-        assert_return(lease, -EINVAL);
-        assert_return(optval, -EINVAL);
-
-        if (!optlen)
-                return 0;
-
-        if (lease->ntp || lease->ntp_fqdn) {
-                log_dhcp6_client(client, "NTP information already provided");
-
-                return 0;
-        }
-
-        log_dhcp6_client(client, "Using deprecated SNTP information");
-
-        r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
-                                        lease->ntp_count,
-                                        &lease->ntp_allocated);
-        if (r < 0)
-                return log_dhcp6_client_errno(client, r, "Invalid SNTP server option: %m");
-
-        lease->ntp_count = r;
-
-        return 0;
-}
-
-int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
-                                 const struct in6_addr **addrs) {
-        assert_return(lease, -EINVAL);
-        assert_return(addrs, -EINVAL);
-
-        if (lease->ntp_count) {
-                *addrs = lease->ntp;
-                return lease->ntp_count;
-        }
-
-        return -ENOENT;
-}
-
-int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
-        assert_return(lease, -EINVAL);
-        assert_return(ntp_fqdn, -EINVAL);
-
-        if (lease->ntp_fqdn_count) {
-                *ntp_fqdn = lease->ntp_fqdn;
-                return lease->ntp_fqdn_count;
-        }
-
-        return -ENOENT;
-}
-
-int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
-                         size_t optlen) {
-        int r;
-        char *fqdn;
-
-        assert_return(lease, -EINVAL);
-        assert_return(optval, -EINVAL);
-
-        if (optlen < 2)
-                return -ENODATA;
-
-        /* Ignore the flags field, it doesn't carry any useful
-           information for clients. */
-        r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
-        if (r < 0)
-                return r;
-
-        return free_and_replace(lease->fqdn, fqdn);
-}
-
-int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) {
-        assert_return(lease, -EINVAL);
-        assert_return(fqdn, -EINVAL);
-
-        if (lease->fqdn) {
-                *fqdn = lease->fqdn;
-                return 0;
-        }
-
-        return -ENOENT;
-}
-
-static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
-        assert(lease);
-
-        free(lease->serverid);
-        dhcp6_lease_free_ia(&lease->ia);
-        dhcp6_lease_free_ia(&lease->pd);
-
-        free(lease->dns);
-        free(lease->fqdn);
-
-        lease->domains = strv_free(lease->domains);
-
-        free(lease->ntp);
-
-        lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
-        return mfree(lease);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
-
-int dhcp6_lease_new(sd_dhcp6_lease **ret) {
-        sd_dhcp6_lease *lease;
-
-        lease = new0(sd_dhcp6_lease, 1);
-        if (!lease)
-                return -ENOMEM;
-
-        lease->n_ref = 1;
-
-        LIST_HEAD_INIT(lease->ia.addresses);
-
-        *ret = lease;
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/core/systemd/src/libsystemd-network/sd-ipv4acd.c
deleted file mode 100644
index 096afbd8..00000000
--- a/src/core/systemd/src/libsystemd-network/sd-ipv4acd.c
+++ /dev/null
@@ -1,543 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <netinet/if_ether.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sd-ipv4acd.h"
-
-#include "alloc-util.h"
-#include "arp-util.h"
-#include "ether-addr-util.h"
-#include "event-util.h"
-#include "fd-util.h"
-#include "format-util.h"
-#include "in-addr-util.h"
-#include "log-link.h"
-#include "random-util.h"
-#include "siphash24.h"
-#include "string-table.h"
-#include "string-util.h"
-#include "time-util.h"
-
-/* Constants from the RFC */
-#define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
-#define PROBE_NUM 3U
-#define PROBE_MIN_USEC (1U * USEC_PER_SEC)
-#define PROBE_MAX_USEC (2U * USEC_PER_SEC)
-#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
-#define ANNOUNCE_NUM 2U
-#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
-#define MAX_CONFLICTS 10U
-#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
-#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
-
-typedef enum IPv4ACDState {
-        IPV4ACD_STATE_INIT,
-        IPV4ACD_STATE_STARTED,
-        IPV4ACD_STATE_WAITING_PROBE,
-        IPV4ACD_STATE_PROBING,
-        IPV4ACD_STATE_WAITING_ANNOUNCE,
-        IPV4ACD_STATE_ANNOUNCING,
-        IPV4ACD_STATE_RUNNING,
-        _IPV4ACD_STATE_MAX,
-        _IPV4ACD_STATE_INVALID = -1
-} IPv4ACDState;
-
-struct sd_ipv4acd {
-        unsigned n_ref;
-
-        IPv4ACDState state;
-        int ifindex;
-        int fd;
-
-        char ifname[IF_NAMESIZE + 1];
-        unsigned n_iteration;
-        unsigned n_conflict;
-
-        sd_event_source *receive_message_event_source;
-        sd_event_source *timer_event_source;
-
-        usec_t defend_window;
-        be32_t address;
-
-        /* External */
-        struct ether_addr mac_addr;
-
-        sd_event *event;
-        int event_priority;
-        sd_ipv4acd_callback_t callback;
-        void* userdata;
-};
-
-#define log_ipv4acd_errno(acd, error, fmt, ...)                         \
-        log_interface_full_errno(sd_ipv4acd_get_ifname(acd), LOG_DEBUG, error, "IPV4ACD: " fmt, ##__VA_ARGS__)
-#define log_ipv4acd(acd, fmt, ...)                      \
-        log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
-
-static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
-        [IPV4ACD_STATE_INIT]             = "init",
-        [IPV4ACD_STATE_STARTED]          = "started",
-        [IPV4ACD_STATE_WAITING_PROBE]    = "waiting-probe",
-        [IPV4ACD_STATE_PROBING]          = "probing",
-        [IPV4ACD_STATE_WAITING_ANNOUNCE] = "waiting-announce",
-        [IPV4ACD_STATE_ANNOUNCING]       = "announcing",
-        [IPV4ACD_STATE_RUNNING]          = "running",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ipv4acd_state, IPv4ACDState);
-
-static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
-        assert(acd);
-        assert(st < _IPV4ACD_STATE_MAX);
-
-        if (st != acd->state)
-                log_ipv4acd(acd, "%s -> %s", ipv4acd_state_to_string(acd->state), ipv4acd_state_to_string(st));
-
-        if (st == acd->state && !reset_counter)
-                acd->n_iteration++;
-        else {
-                acd->state = st;
-                acd->n_iteration = 0;
-        }
-}
-
-static void ipv4acd_reset(sd_ipv4acd *acd) {
-        assert(acd);
-
-        (void) event_source_disable(acd->timer_event_source);
-        acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
-
-        acd->fd = safe_close(acd->fd);
-
-        ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
-}
-
-static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
-        assert(acd);
-
-        acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
-
-        ipv4acd_reset(acd);
-        sd_ipv4acd_detach_event(acd);
-
-        return mfree(acd);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
-
-int sd_ipv4acd_new(sd_ipv4acd **ret) {
-        _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
-
-        assert_return(ret, -EINVAL);
-
-        acd = new(sd_ipv4acd, 1);
-        if (!acd)
-                return -ENOMEM;
-
-        *acd = (sd_ipv4acd) {
-                .n_ref = 1,
-                .state = IPV4ACD_STATE_INIT,
-                .ifindex = -1,
-                .fd = -1,
-        };
-
-        *ret = TAKE_PTR(acd);
-
-        return 0;
-}
-
-static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
-        assert(acd);
-
-        if (!acd->callback)
-                return;
-
-        acd->callback(acd, event, acd->userdata);
-}
-
-int sd_ipv4acd_stop(sd_ipv4acd *acd) {
-        IPv4ACDState old_state;
-
-        if (!acd)
-                return 0;
-
-        old_state = acd->state;
-
-        ipv4acd_reset(acd);
-
-        if (old_state == IPV4ACD_STATE_INIT)
-                return 0;
-
-        log_ipv4acd(acd, "STOPPED");
-
-        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
-
-        return 0;
-}
-
-static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
-
-static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
-        usec_t next_timeout, time_now;
-
-        assert(acd);
-
-        next_timeout = usec;
-
-        if (random_usec > 0)
-                next_timeout += (usec_t) random_u64() % random_usec;
-
-        assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-
-        return event_reset_time(acd->event, &acd->timer_event_source,
-                                clock_boottime_or_monotonic(),
-                                time_now + next_timeout, 0,
-                                ipv4acd_on_timeout, acd,
-                                acd->event_priority, "ipv4acd-timer", true);
-}
-
-static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
-        assert(acd);
-        assert(arp);
-
-        /* see the BPF */
-        if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
-                return true;
-
-        /* the TPA matched instead of the SPA, this is not a conflict */
-        return false;
-}
-
-static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_ipv4acd *acd = userdata;
-        int r = 0;
-
-        assert(acd);
-
-        switch (acd->state) {
-
-        case IPV4ACD_STATE_STARTED:
-                ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
-
-                if (acd->n_conflict >= MAX_CONFLICTS) {
-                        char ts[FORMAT_TIMESPAN_MAX];
-                        log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
-
-                        r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
-                        if (r < 0)
-                                goto fail;
-                } else {
-                        r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
-                        if (r < 0)
-                                goto fail;
-                }
-
-                break;
-
-        case IPV4ACD_STATE_WAITING_PROBE:
-        case IPV4ACD_STATE_PROBING:
-                /* Send a probe */
-                r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
-                if (r < 0) {
-                        log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
-                        goto fail;
-                } else {
-                        _cleanup_free_ char *address = NULL;
-                        union in_addr_union addr = { .in.s_addr = acd->address };
-
-                        (void) in_addr_to_string(AF_INET, &addr, &address);
-                        log_ipv4acd(acd, "Probing %s", strna(address));
-                }
-
-                if (acd->n_iteration < PROBE_NUM - 2) {
-                        ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
-
-                        r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
-                        if (r < 0)
-                                goto fail;
-                } else {
-                        ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
-
-                        r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
-                        if (r < 0)
-                                goto fail;
-                }
-
-                break;
-
-        case IPV4ACD_STATE_ANNOUNCING:
-                if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
-                        ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
-                        break;
-                }
-
-                _fallthrough_;
-        case IPV4ACD_STATE_WAITING_ANNOUNCE:
-                /* Send announcement packet */
-                r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
-                if (r < 0) {
-                        log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
-                        goto fail;
-                } else
-                        log_ipv4acd(acd, "ANNOUNCE");
-
-                ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
-
-                r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
-                if (r < 0)
-                        goto fail;
-
-                if (acd->n_iteration == 0) {
-                        acd->n_conflict = 0;
-                        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Invalid state.");
-        }
-
-        return 0;
-
-fail:
-        sd_ipv4acd_stop(acd);
-        return 0;
-}
-
-static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
-        _cleanup_free_ char *address = NULL;
-        union in_addr_union addr = { .in.s_addr = acd->address };
-
-        assert(acd);
-
-        acd->n_conflict++;
-
-        (void) in_addr_to_string(AF_INET, &addr, &address);
-        log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
-
-        ipv4acd_reset(acd);
-        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
-}
-
-static int ipv4acd_on_packet(
-                sd_event_source *s,
-                int fd,
-                uint32_t revents,
-                void *userdata) {
-
-        sd_ipv4acd *acd = userdata;
-        struct ether_arp packet;
-        ssize_t n;
-        int r;
-
-        assert(s);
-        assert(acd);
-        assert(fd >= 0);
-
-        n = recv(fd, &packet, sizeof(struct ether_arp), 0);
-        if (n < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
-                        return 0;
-
-                log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
-                goto fail;
-        }
-        if ((size_t) n != sizeof(struct ether_arp)) {
-                log_ipv4acd(acd, "Ignoring too short ARP packet.");
-                return 0;
-        }
-
-        switch (acd->state) {
-
-        case IPV4ACD_STATE_ANNOUNCING:
-        case IPV4ACD_STATE_RUNNING:
-
-                if (ipv4acd_arp_conflict(acd, &packet)) {
-                        usec_t ts;
-
-                        assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
-                        /* Defend address */
-                        if (ts > acd->defend_window) {
-                                acd->defend_window = ts + DEFEND_INTERVAL_USEC;
-                                r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
-                                if (r < 0) {
-                                        log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
-                                        goto fail;
-                                } else
-                                        log_ipv4acd(acd, "DEFEND");
-
-                        } else
-                                ipv4acd_on_conflict(acd);
-                }
-                break;
-
-        case IPV4ACD_STATE_WAITING_PROBE:
-        case IPV4ACD_STATE_PROBING:
-        case IPV4ACD_STATE_WAITING_ANNOUNCE:
-                /* BPF ensures this packet indicates a conflict */
-                ipv4acd_on_conflict(acd);
-                break;
-
-        default:
-                assert_not_reached("Invalid state.");
-        }
-
-        return 0;
-
-fail:
-        sd_ipv4acd_stop(acd);
-        return 0;
-}
-
-int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
-        char ifname[IF_NAMESIZE + 1];
-
-        assert_return(acd, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
-
-        if (!format_ifname(ifindex, ifname))
-                return -ENODEV;
-
-        strcpy(acd->ifname, ifname);
-        acd->ifindex = ifindex;
-
-        return 0;
-}
-
-int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd) {
-        if (!acd)
-                return -EINVAL;
-
-        return acd->ifindex;
-}
-
-const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) {
-        if (!acd)
-                return NULL;
-
-        return empty_to_null(acd->ifname);
-}
-
-int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
-        assert_return(acd, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
-
-        acd->mac_addr = *addr;
-
-        return 0;
-}
-
-int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
-        assert_return(acd, -EINVAL);
-
-        acd->event = sd_event_unref(acd->event);
-
-        return 0;
-}
-
-int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
-        int r;
-
-        assert_return(acd, -EINVAL);
-        assert_return(!acd->event, -EBUSY);
-
-        if (event)
-                acd->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&acd->event);
-                if (r < 0)
-                        return r;
-        }
-
-        acd->event_priority = priority;
-
-        return 0;
-}
-
-int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
-        assert_return(acd, -EINVAL);
-
-        acd->callback = cb;
-        acd->userdata = userdata;
-
-        return 0;
-}
-
-int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
-        assert_return(acd, -EINVAL);
-        assert_return(address, -EINVAL);
-        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
-
-        acd->address = address->s_addr;
-
-        return 0;
-}
-
-int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
-        assert_return(acd, -EINVAL);
-        assert_return(address, -EINVAL);
-
-        address->s_addr = acd->address;
-
-        return 0;
-}
-
-int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
-        assert_return(acd, false);
-
-        return acd->state != IPV4ACD_STATE_INIT;
-}
-
-int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
-        int r;
-
-        assert_return(acd, -EINVAL);
-        assert_return(acd->event, -EINVAL);
-        assert_return(acd->ifindex > 0, -EINVAL);
-        assert_return(acd->address != 0, -EINVAL);
-        assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
-        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
-
-        r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
-        if (r < 0)
-                return r;
-
-        CLOSE_AND_REPLACE(acd->fd, r);
-        acd->defend_window = 0;
-
-        if (reset_conflicts)
-                acd->n_conflict = 0;
-
-        r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
-        if (r < 0)
-                goto fail;
-
-        r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
-        if (r < 0)
-                goto fail;
-
-        (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
-
-        r = ipv4acd_set_next_wakeup(acd, 0, 0);
-        if (r < 0)
-                goto fail;
-
-        ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
-        return 0;
-
-fail:
-        ipv4acd_reset(acd);
-        return r;
-}
diff --git a/src/core/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/core/systemd/src/libsystemd-network/sd-ipv4ll.c
deleted file mode 100644
index c5e7a91f..00000000
--- a/src/core/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ /dev/null
@@ -1,342 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sd-id128.h"
-#include "sd-ipv4acd.h"
-#include "sd-ipv4ll.h"
-
-#include "alloc-util.h"
-#include "ether-addr-util.h"
-#include "in-addr-util.h"
-#include "log-link.h"
-#include "random-util.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "string-util.h"
-#include "util.h"
-
-#define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
-#define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
-
-#define IPV4LL_DONT_DESTROY(ll) \
-        _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
-
-struct sd_ipv4ll {
-        unsigned n_ref;
-
-        sd_ipv4acd *acd;
-
-        be32_t address; /* the address pushed to ACD */
-        struct ether_addr mac;
-
-        struct {
-                le64_t value;
-                le64_t generation;
-        } seed;
-        bool seed_set;
-
-        /* External */
-        be32_t claimed_address;
-
-        sd_ipv4ll_callback_t callback;
-        void* userdata;
-};
-
-#define log_ipv4ll_errno(ll, error, fmt, ...)                           \
-        log_interface_full_errno(sd_ipv4ll_get_ifname(ll), LOG_DEBUG, error, "IPV4LL: " fmt, ##__VA_ARGS__)
-#define log_ipv4ll(ll, fmt, ...)                        \
-        log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
-
-static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
-
-static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) {
-        assert(ll);
-
-        sd_ipv4acd_unref(ll->acd);
-        return mfree(ll);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll, sd_ipv4ll, ipv4ll_free);
-
-int sd_ipv4ll_new(sd_ipv4ll **ret) {
-        _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        ll = new0(sd_ipv4ll, 1);
-        if (!ll)
-                return -ENOMEM;
-
-        ll->n_ref = 1;
-
-        r = sd_ipv4acd_new(&ll->acd);
-        if (r < 0)
-                return r;
-
-        r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(ll);
-
-        return 0;
-}
-
-int sd_ipv4ll_stop(sd_ipv4ll *ll) {
-        if (!ll)
-                return 0;
-
-        return sd_ipv4acd_stop(ll->acd);
-}
-
-int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) {
-        assert_return(ll, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
-
-        return sd_ipv4acd_set_ifindex(ll->acd, ifindex);
-}
-
-int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll) {
-        if (!ll)
-                return -EINVAL;
-
-        return sd_ipv4acd_get_ifindex(ll->acd);
-}
-
-const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll) {
-        if (!ll)
-                return NULL;
-
-        return sd_ipv4acd_get_ifname(ll->acd);
-}
-
-int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
-        int r;
-
-        assert_return(ll, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
-
-        r = sd_ipv4acd_set_mac(ll->acd, addr);
-        if (r < 0)
-                return r;
-
-        ll->mac = *addr;
-        return 0;
-}
-
-int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
-        assert_return(ll, -EINVAL);
-
-        return sd_ipv4acd_detach_event(ll->acd);
-}
-
-int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
-        assert_return(ll, -EINVAL);
-
-        return sd_ipv4acd_attach_event(ll->acd, event, priority);
-}
-
-int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
-        assert_return(ll, -EINVAL);
-
-        ll->callback = cb;
-        ll->userdata = userdata;
-
-        return 0;
-}
-
-int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
-        assert_return(ll, -EINVAL);
-        assert_return(address, -EINVAL);
-
-        if (ll->claimed_address == 0)
-                return -ENOENT;
-
-        address->s_addr = ll->claimed_address;
-
-        return 0;
-}
-
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
-        assert_return(ll, -EINVAL);
-        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
-
-        ll->seed.value = htole64(seed);
-        ll->seed_set = true;
-
-        return 0;
-}
-
-int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
-        assert_return(ll, false);
-
-        return sd_ipv4acd_is_running(ll->acd);
-}
-
-static bool ipv4ll_address_is_valid(const struct in_addr *address) {
-        assert(address);
-
-        if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
-                return false;
-
-        return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
-}
-
-int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
-        int r;
-
-        assert_return(ll, -EINVAL);
-        assert_return(address, -EINVAL);
-        assert_return(ipv4ll_address_is_valid(address), -EINVAL);
-
-        r = sd_ipv4acd_set_address(ll->acd, address);
-        if (r < 0)
-                return r;
-
-        ll->address = address->s_addr;
-
-        return 0;
-}
-
-#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
-
-static int ipv4ll_pick_address(sd_ipv4ll *ll) {
-        _cleanup_free_ char *address = NULL;
-        be32_t addr;
-
-        assert(ll);
-
-        do {
-                uint64_t h;
-
-                h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes);
-
-                /* Increase the generation counter by one */
-                ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1);
-
-                addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK);
-        } while (addr == ll->address ||
-                 IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U));
-
-        (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address);
-        log_ipv4ll(ll, "Picked new IP address %s.", strna(address));
-
-        return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
-}
-
-#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
-
-static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) {
-        int r;
-        bool picked_address = false;
-
-        assert_return(ll, -EINVAL);
-        assert_return(!ether_addr_is_null(&ll->mac), -EINVAL);
-
-        /* If no random seed is set, generate some from the MAC address */
-        if (!ll->seed_set)
-                ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes));
-
-        if (reset_generation)
-                ll->seed.generation = 0;
-
-        if (ll->address == 0) {
-                r = ipv4ll_pick_address(ll);
-                if (r < 0)
-                        return r;
-
-                picked_address = true;
-        }
-
-        r = sd_ipv4acd_start(ll->acd, reset_generation);
-        if (r < 0) {
-
-                /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
-                 * retry, and we want the new data to take effect when picking an address. */
-                if (picked_address)
-                        ll->address = 0;
-
-                return r;
-        }
-
-        return 1;
-}
-
-int sd_ipv4ll_start(sd_ipv4ll *ll) {
-        assert_return(ll, -EINVAL);
-
-        if (sd_ipv4ll_is_running(ll))
-                return 0;
-
-        return ipv4ll_start_internal(ll, true);
-}
-
-int sd_ipv4ll_restart(sd_ipv4ll *ll) {
-        ll->address = 0;
-
-        return ipv4ll_start_internal(ll, false);
-}
-
-static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
-        assert(ll);
-
-        if (ll->callback)
-                ll->callback(ll, event, ll->userdata);
-}
-
-void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
-        sd_ipv4ll *ll = userdata;
-        IPV4LL_DONT_DESTROY(ll);
-        int r;
-
-        assert(acd);
-        assert(ll);
-
-        switch (event) {
-
-        case SD_IPV4ACD_EVENT_STOP:
-                ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
-                ll->claimed_address = 0;
-                break;
-
-        case SD_IPV4ACD_EVENT_BIND:
-                ll->claimed_address = ll->address;
-                ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
-                break;
-
-        case SD_IPV4ACD_EVENT_CONFLICT:
-                /* if an address was already bound we must call up to the
-                   user to handle this, otherwise we just try again */
-                if (ll->claimed_address != 0) {
-                        ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
-
-                        ll->claimed_address = 0;
-                } else {
-                        r = sd_ipv4ll_restart(ll);
-                        if (r < 0)
-                                goto error;
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Invalid IPv4ACD event.");
-        }
-
-        return;
-
-error:
-        ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
-}
diff --git a/src/core/systemd/src/libsystemd-network/sd-lldp.c b/src/core/systemd/src/libsystemd-network/sd-lldp.c
deleted file mode 100644
index b12d8e27..00000000
--- a/src/core/systemd/src/libsystemd-network/sd-lldp.c
+++ /dev/null
@@ -1,500 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <linux/sockios.h>
-#include <sys/ioctl.h>
-
-#include "sd-lldp.h"
-
-#include "alloc-util.h"
-#include "ether-addr-util.h"
-#include "event-util.h"
-#include "fd-util.h"
-#include "lldp-internal.h"
-#include "lldp-neighbor.h"
-#include "lldp-network.h"
-#include "memory-util.h"
-#include "socket-util.h"
-#include "sort-util.h"
-#include "string-table.h"
-
-#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
-
-static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
-        [SD_LLDP_EVENT_ADDED]   = "added",
-        [SD_LLDP_EVENT_REMOVED] = "removed",
-        [SD_LLDP_EVENT_UPDATED]   = "updated",
-        [SD_LLDP_EVENT_REFRESHED] = "refreshed",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
-
-static void lldp_flush_neighbors(sd_lldp *lldp) {
-        assert(lldp);
-
-        hashmap_clear(lldp->neighbor_by_id);
-}
-
-static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
-        assert(lldp);
-        assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
-
-        if (!lldp->callback) {
-                log_lldp("Received '%s' event.", lldp_event_to_string(event));
-                return;
-        }
-
-        log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event));
-        lldp->callback(lldp, event, n, lldp->userdata);
-}
-
-static int lldp_make_space(sd_lldp *lldp, size_t extra) {
-        usec_t t = USEC_INFINITY;
-        bool changed = false;
-
-        assert(lldp);
-
-        /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
-         * are free. */
-
-        for (;;) {
-                _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
-
-                n = prioq_peek(lldp->neighbor_by_expiry);
-                if (!n)
-                        break;
-
-                sd_lldp_neighbor_ref(n);
-
-                if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
-                        goto remove_one;
-
-                if (t == USEC_INFINITY)
-                        t = now(clock_boottime_or_monotonic());
-
-                if (n->until > t)
-                        break;
-
-        remove_one:
-                lldp_neighbor_unlink(n);
-                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
-                changed = true;
-        }
-
-        return changed;
-}
-
-static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
-        assert(lldp);
-        assert(n);
-
-        /* Don't keep data with a zero TTL */
-        if (n->ttl <= 0)
-                return false;
-
-        /* Filter out data from the filter address */
-        if (!ether_addr_is_null(&lldp->filter_address) &&
-            ether_addr_equal(&lldp->filter_address, &n->source_address))
-                return false;
-
-        /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
-         * no caps field set. */
-        if (n->has_capabilities &&
-            (n->enabled_capabilities & lldp->capability_mask) == 0)
-                return false;
-
-        /* Keep everything else */
-        return true;
-}
-
-static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
-
-static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
-        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
-        bool keep;
-        int r;
-
-        assert(lldp);
-        assert(n);
-        assert(!n->lldp);
-
-        keep = lldp_keep_neighbor(lldp, n);
-
-        /* First retrieve the old entry for this MSAP */
-        old = hashmap_get(lldp->neighbor_by_id, &n->id);
-        if (old) {
-                sd_lldp_neighbor_ref(old);
-
-                if (!keep) {
-                        lldp_neighbor_unlink(old);
-                        lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
-                        return 0;
-                }
-
-                if (lldp_neighbor_equal(n, old)) {
-                        /* Is this equal, then restart the TTL counter, but don't do anything else. */
-                        old->timestamp = n->timestamp;
-                        lldp_start_timer(lldp, old);
-                        lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
-                        return 0;
-                }
-
-                /* Data changed, remove the old entry, and add a new one */
-                lldp_neighbor_unlink(old);
-
-        } else if (!keep)
-                return 0;
-
-        /* Then, make room for at least one new neighbor */
-        lldp_make_space(lldp, 1);
-
-        r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
-        if (r < 0)
-                goto finish;
-
-        r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
-        if (r < 0) {
-                assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
-                goto finish;
-        }
-
-        n->lldp = lldp;
-
-        lldp_start_timer(lldp, n);
-        lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
-
-        return 1;
-
-finish:
-        if (old)
-                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
-
-        return r;
-}
-
-static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
-        int r;
-
-        assert(lldp);
-        assert(n);
-
-        r = lldp_neighbor_parse(n);
-        if (r == -EBADMSG) /* Ignore bad messages */
-                return 0;
-        if (r < 0)
-                return r;
-
-        r = lldp_add_neighbor(lldp, n);
-        if (r < 0) {
-                log_lldp_errno(r, "Failed to add datagram. Ignoring.");
-                return 0;
-        }
-
-        log_lldp("Successfully processed LLDP datagram.");
-        return 0;
-}
-
-static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
-        ssize_t space, length;
-        sd_lldp *lldp = userdata;
-        struct timespec ts;
-
-        assert(fd >= 0);
-        assert(lldp);
-
-        space = next_datagram_size_fd(fd);
-        if (space < 0)
-                return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
-
-        n = lldp_neighbor_new(space);
-        if (!n)
-                return -ENOMEM;
-
-        length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
-        if (length < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
-                        return 0;
-
-                return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
-        }
-
-        if ((size_t) length != n->raw_size) {
-                log_lldp("Packet size mismatch.");
-                return -EINVAL;
-        }
-
-        /* Try to get the timestamp of this packet if it is known */
-        if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
-                triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
-        else
-                triple_timestamp_get(&n->timestamp);
-
-        return lldp_handle_datagram(lldp, n);
-}
-
-static void lldp_reset(sd_lldp *lldp) {
-        assert(lldp);
-
-        (void) event_source_disable(lldp->timer_event_source);
-        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
-        lldp->fd = safe_close(lldp->fd);
-}
-
-_public_ int sd_lldp_start(sd_lldp *lldp) {
-        int r;
-
-        assert_return(lldp, -EINVAL);
-        assert_return(lldp->event, -EINVAL);
-        assert_return(lldp->ifindex > 0, -EINVAL);
-
-        if (lldp->fd >= 0)
-                return 0;
-
-        assert(!lldp->io_event_source);
-
-        lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
-        if (lldp->fd < 0)
-                return lldp->fd;
-
-        r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
-        if (r < 0)
-                goto fail;
-
-        r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
-        if (r < 0)
-                goto fail;
-
-        (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
-
-        log_lldp("Started LLDP client");
-        return 1;
-
-fail:
-        lldp_reset(lldp);
-        return r;
-}
-
-_public_ int sd_lldp_stop(sd_lldp *lldp) {
-        if (!lldp)
-                return 0;
-
-        if (lldp->fd < 0)
-                return 0;
-
-        log_lldp("Stopping LLDP client");
-
-        lldp_reset(lldp);
-        lldp_flush_neighbors(lldp);
-
-        return 1;
-}
-
-_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
-        int r;
-
-        assert_return(lldp, -EINVAL);
-        assert_return(lldp->fd < 0, -EBUSY);
-        assert_return(!lldp->event, -EBUSY);
-
-        if (event)
-                lldp->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&lldp->event);
-                if (r < 0)
-                        return r;
-        }
-
-        lldp->event_priority = priority;
-
-        return 0;
-}
-
-_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
-
-        assert_return(lldp, -EINVAL);
-        assert_return(lldp->fd < 0, -EBUSY);
-
-        lldp->event = sd_event_unref(lldp->event);
-        return 0;
-}
-
-_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
-        assert_return(lldp, NULL);
-
-        return lldp->event;
-}
-
-_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
-        assert_return(lldp, -EINVAL);
-
-        lldp->callback = cb;
-        lldp->userdata = userdata;
-
-        return 0;
-}
-
-_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
-        assert_return(lldp, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(lldp->fd < 0, -EBUSY);
-
-        lldp->ifindex = ifindex;
-        return 0;
-}
-
-static sd_lldp* lldp_free(sd_lldp *lldp) {
-        assert(lldp);
-
-        lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
-
-        lldp_reset(lldp);
-        sd_lldp_detach_event(lldp);
-        lldp_flush_neighbors(lldp);
-
-        hashmap_free(lldp->neighbor_by_id);
-        prioq_free(lldp->neighbor_by_expiry);
-        return mfree(lldp);
-}
-
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
-
-_public_ int sd_lldp_new(sd_lldp **ret) {
-        _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        lldp = new(sd_lldp, 1);
-        if (!lldp)
-                return -ENOMEM;
-
-        *lldp = (sd_lldp) {
-                .n_ref = 1,
-                .fd = -1,
-                .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
-                .capability_mask = (uint16_t) -1,
-        };
-
-        lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
-        if (!lldp->neighbor_by_id)
-                return -ENOMEM;
-
-        r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(lldp);
-
-        return 0;
-}
-
-static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
-        return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
-}
-
-static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_lldp *lldp = userdata;
-        int r;
-
-        r = lldp_make_space(lldp, 0);
-        if (r < 0)
-                return log_lldp_errno(r, "Failed to make space: %m");
-
-        r = lldp_start_timer(lldp, NULL);
-        if (r < 0)
-                return log_lldp_errno(r, "Failed to restart timer: %m");
-
-        return 0;
-}
-
-static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
-        sd_lldp_neighbor *n;
-
-        assert(lldp);
-
-        if (neighbor)
-                lldp_neighbor_start_ttl(neighbor);
-
-        n = prioq_peek(lldp->neighbor_by_expiry);
-        if (!n)
-                return event_source_disable(lldp->timer_event_source);
-
-        if (!lldp->event)
-                return 0;
-
-        return event_reset_time(lldp->event, &lldp->timer_event_source,
-                                clock_boottime_or_monotonic(),
-                                n->until, 0,
-                                on_timer_event, lldp,
-                                lldp->event_priority, "lldp-timer", true);
-}
-
-_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
-        sd_lldp_neighbor **l = NULL, *n;
-        int k = 0, r;
-
-        assert_return(lldp, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
-                *ret = NULL;
-                return 0;
-        }
-
-        l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
-        if (!l)
-                return -ENOMEM;
-
-        r = lldp_start_timer(lldp, NULL);
-        if (r < 0) {
-                free(l);
-                return r;
-        }
-
-        HASHMAP_FOREACH(n, lldp->neighbor_by_id)
-                l[k++] = sd_lldp_neighbor_ref(n);
-
-        assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
-
-        /* Return things in a stable order */
-        typesafe_qsort(l, k, neighbor_compare_func);
-        *ret = l;
-
-        return k;
-}
-
-_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
-        assert_return(lldp, -EINVAL);
-        assert_return(m > 0, -EINVAL);
-
-        lldp->neighbors_max = m;
-        lldp_make_space(lldp, 0);
-
-        return 0;
-}
-
-_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
-        assert_return(lldp, -EINVAL);
-        assert_return(mask != 0, -EINVAL);
-
-        lldp->capability_mask = mask;
-
-        return 0;
-}
-
-_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
-        assert_return(lldp, -EINVAL);
-
-        /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
-         * that our own can be filtered out here. */
-
-        if (addr)
-                lldp->filter_address = *addr;
-        else
-                zero(lldp->filter_address);
-
-        return 0;
-}
diff --git a/src/core/systemd/src/libsystemd/sd-event/event-source.h b/src/core/systemd/src/libsystemd/sd-event/event-source.h
deleted file mode 100644
index f0d2a1b9..00000000
--- a/src/core/systemd/src/libsystemd/sd-event/event-source.h
+++ /dev/null
@@ -1,220 +0,0 @@
-#pragma once
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <sys/wait.h>
-
-#include "sd-event.h"
-
-#include "fs-util.h"
-#include "hashmap.h"
-#include "list.h"
-#include "prioq.h"
-#include "ratelimit.h"
-
-typedef enum EventSourceType {
-        SOURCE_IO,
-        SOURCE_TIME_REALTIME,
-        SOURCE_TIME_BOOTTIME,
-        SOURCE_TIME_MONOTONIC,
-        SOURCE_TIME_REALTIME_ALARM,
-        SOURCE_TIME_BOOTTIME_ALARM,
-        SOURCE_SIGNAL,
-        SOURCE_CHILD,
-        SOURCE_DEFER,
-        SOURCE_POST,
-        SOURCE_EXIT,
-        SOURCE_WATCHDOG,
-        SOURCE_INOTIFY,
-        _SOURCE_EVENT_SOURCE_TYPE_MAX,
-        _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
-} EventSourceType;
-
-/* All objects we use in epoll events start with this value, so that
- * we know how to dispatch it */
-typedef enum WakeupType {
-        WAKEUP_NONE,
-        WAKEUP_EVENT_SOURCE, /* either I/O or pidfd wakeup */
-        WAKEUP_CLOCK_DATA,
-        WAKEUP_SIGNAL_DATA,
-        WAKEUP_INOTIFY_DATA,
-        _WAKEUP_TYPE_MAX,
-        _WAKEUP_TYPE_INVALID = -1,
-} WakeupType;
-
-struct inode_data;
-
-struct sd_event_source {
-        WakeupType wakeup;
-
-        unsigned n_ref;
-
-        sd_event *event;
-        void *userdata;
-        sd_event_handler_t prepare;
-
-        char *description;
-
-        EventSourceType type:5;
-        signed int enabled:3;
-        bool pending:1;
-        bool dispatching:1;
-        bool floating:1;
-        bool exit_on_failure:1;
-        bool ratelimited:1;
-
-        int64_t priority;
-        unsigned pending_index;
-        unsigned prepare_index;
-        uint64_t pending_iteration;
-        uint64_t prepare_iteration;
-
-        sd_event_destroy_t destroy_callback;
-
-        LIST_FIELDS(sd_event_source, sources);
-
-        RateLimit rate_limit;
-
-        /* These are primarily fields relevant for time event sources, but since any event source can
-         * effectively become one when rate-limited, this is part of the common fields. */
-        unsigned earliest_index;
-        unsigned latest_index;
-
-        union {
-                struct {
-                        sd_event_io_handler_t callback;
-                        int fd;
-                        uint32_t events;
-                        uint32_t revents;
-                        bool registered:1;
-                        bool owned:1;
-                } io;
-                struct {
-                        sd_event_time_handler_t callback;
-                        usec_t next, accuracy;
-                } time;
-                struct {
-                        sd_event_signal_handler_t callback;
-                        struct signalfd_siginfo siginfo;
-                        int sig;
-                } signal;
-                struct {
-                        sd_event_child_handler_t callback;
-                        siginfo_t siginfo;
-                        pid_t pid;
-                        int options;
-                        int pidfd;
-                        bool registered:1; /* whether the pidfd is registered in the epoll */
-                        bool pidfd_owned:1; /* close pidfd when event source is freed */
-                        bool process_owned:1; /* kill+reap process when event source is freed */
-                        bool exited:1; /* true if process exited (i.e. if there's value in SIGKILLing it if we want to get rid of it) */
-                        bool waited:1; /* true if process was waited for (i.e. if there's value in waitid(P_PID)'ing it if we want to get rid of it) */
-                } child;
-                struct {
-                        sd_event_handler_t callback;
-                } defer;
-                struct {
-                        sd_event_handler_t callback;
-                } post;
-                struct {
-                        sd_event_handler_t callback;
-                        unsigned prioq_index;
-                } exit;
-                struct {
-                        sd_event_inotify_handler_t callback;
-                        uint32_t mask;
-                        struct inode_data *inode_data;
-                        LIST_FIELDS(sd_event_source, by_inode_data);
-                } inotify;
-        };
-};
-
-struct clock_data {
-        WakeupType wakeup;
-        int fd;
-
-        /* For all clocks we maintain two priority queues each, one
-         * ordered for the earliest times the events may be
-         * dispatched, and one ordered by the latest times they must
-         * have been dispatched. The range between the top entries in
-         * the two prioqs is the time window we can freely schedule
-         * wakeups in */
-
-        Prioq *earliest;
-        Prioq *latest;
-        usec_t next;
-
-        bool needs_rearm:1;
-};
-
-struct signal_data {
-        WakeupType wakeup;
-
-        /* For each priority we maintain one signal fd, so that we
-         * only have to dequeue a single event per priority at a
-         * time. */
-
-        int fd;
-        int64_t priority;
-        sigset_t sigset;
-        sd_event_source *current;
-};
-
-/* A structure listing all event sources currently watching a specific inode */
-struct inode_data {
-        /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */
-        ino_t ino;
-        dev_t dev;
-
-        /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can
-         * rearrange the priority still until then, as we need the original inode to change the priority as we need to
-         * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the
-         * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of
-         * the sd-event object, so that it is efficient to close everything, before entering the next event loop
-         * iteration. */
-        int fd;
-
-        /* The inotify "watch descriptor" */
-        int wd;
-
-        /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has
-         * most recently been set on the watch descriptor. */
-        uint32_t combined_mask;
-
-        /* All event sources subscribed to this inode */
-        LIST_HEAD(sd_event_source, event_sources);
-
-        /* The inotify object we watch this inode with */
-        struct inotify_data *inotify_data;
-
-        /* A linked list of all inode data objects with fds to close (see above) */
-        LIST_FIELDS(struct inode_data, to_close);
-};
-
-/* A structure encapsulating an inotify fd */
-struct inotify_data {
-        WakeupType wakeup;
-
-        /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at
-         * a time */
-
-        int fd;
-        int64_t priority;
-
-        Hashmap *inodes; /* The inode_data structures keyed by dev+ino */
-        Hashmap *wd;     /* The inode_data structures keyed by the watch descriptor for each */
-
-        /* The buffer we read inotify events into */
-        union inotify_event_buffer buffer;
-        size_t buffer_filled; /* fill level of the buffer */
-
-        /* How many event sources are currently marked pending for this inotify. We won't read new events off the
-         * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing
-         * the events locally if they can't be coalesced). */
-        unsigned n_pending;
-
-        /* A linked list of all inotify objects with data already read, that still need processing. We keep this list
-         * to make it efficient to figure out what inotify objects to process data on next. */
-        LIST_FIELDS(struct inotify_data, buffered);
-};
diff --git a/src/core/systemd/src/libsystemd/sd-event/event-util.c b/src/core/systemd/src/libsystemd/sd-event/event-util.c
deleted file mode 100644
index 7cc55be1..00000000
--- a/src/core/systemd/src/libsystemd/sd-event/event-util.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-
-#include "event-source.h"
-#include "event-util.h"
-#include "log.h"
-#include "string-util.h"
-
-int event_reset_time(
-                sd_event *e,
-                sd_event_source **s,
-                clockid_t clock,
-                uint64_t usec,
-                uint64_t accuracy,
-                sd_event_time_handler_t callback,
-                void *userdata,
-                int64_t priority,
-                const char *description,
-                bool force_reset) {
-
-        bool created = false;
-        int enabled, r;
-        clockid_t c;
-
-        assert(e);
-        assert(s);
-
-        if (*s) {
-                if (!force_reset) {
-                        r = sd_event_source_get_enabled(*s, &enabled);
-                        if (r < 0)
-                                return log_debug_errno(r, "sd-event: Failed to query whether event source \"%s\" is enabled or not: %m",
-                                                       strna((*s)->description ?: description));
-
-                        if (enabled != SD_EVENT_OFF)
-                                return 0;
-                }
-
-                r = sd_event_source_get_time_clock(*s, &c);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-event: Failed to get clock id of event source \"%s\": %m", strna((*s)->description ?: description));
-
-                if (c != clock)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "sd-event: Current clock id %i of event source \"%s\" is different from specified one %i.",
-                                               (int)c,
-                                               strna((*s)->description ? : description),
-                                               (int)clock);
-
-                r = sd_event_source_set_time(*s, usec);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-event: Failed to set time for event source \"%s\": %m", strna((*s)->description ?: description));
-
-                r = sd_event_source_set_time_accuracy(*s, accuracy);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-event: Failed to set accuracy for event source \"%s\": %m", strna((*s)->description ?: description));
-
-                /* callback function is not updated, as we do not have sd_event_source_set_time_callback(). */
-
-                (void) sd_event_source_set_userdata(*s, userdata);
-
-                r = sd_event_source_set_enabled(*s, SD_EVENT_ONESHOT);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-event: Failed to enable event source \"%s\": %m", strna((*s)->description ?: description));
-        } else {
-                r = sd_event_add_time(e, s, clock, usec, accuracy, callback, userdata);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-event: Failed to create timer event \"%s\": %m", strna(description));
-
-                created = true;
-        }
-
-        r = sd_event_source_set_priority(*s, priority);
-        if (r < 0)
-                return log_debug_errno(r, "sd-event: Failed to set priority for event source \"%s\": %m", strna((*s)->description ?: description));
-
-        if (description) {
-                r = sd_event_source_set_description(*s, description);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-event: Failed to set description for event source \"%s\": %m", description);
-        }
-
-        return created;
-}
-
-int event_source_disable(sd_event_source *s) {
-        if (!s)
-                return 0;
-
-        return sd_event_source_set_enabled(s, SD_EVENT_OFF);
-}
-
-int event_source_is_enabled(sd_event_source *s) {
-        if (!s)
-                return false;
-
-        return sd_event_source_get_enabled(s, NULL);
-}
diff --git a/src/core/systemd/src/libsystemd/sd-event/event-util.h b/src/core/systemd/src/libsystemd/sd-event/event-util.h
deleted file mode 100644
index c8f97bc8..00000000
--- a/src/core/systemd/src/libsystemd/sd-event/event-util.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <stdbool.h>
-
-#include "sd-event.h"
-
-int event_reset_time(sd_event *e, sd_event_source **s,
-                     clockid_t clock, uint64_t usec, uint64_t accuracy,
-                     sd_event_time_handler_t callback, void *userdata,
-                     int64_t priority, const char *description, bool force_reset);
-int event_source_disable(sd_event_source *s);
-int event_source_is_enabled(sd_event_source *s);
diff --git a/src/core/systemd/src/libsystemd/sd-event/sd-event.c b/src/core/systemd/src/libsystemd/sd-event/sd-event.c
deleted file mode 100644
index dda4be00..00000000
--- a/src/core/systemd/src/libsystemd/sd-event/sd-event.c
+++ /dev/null
@@ -1,4329 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <sys/wait.h>
-
-#include "sd-daemon.h"
-#include "sd-event.h"
-#include "sd-id128.h"
-
-#include "alloc-util.h"
-#include "env-util.h"
-#include "event-source.h"
-#include "fd-util.h"
-#include "fs-util.h"
-#include "hashmap.h"
-#include "list.h"
-#include "macro.h"
-#include "memory-util.h"
-#include "missing_syscall.h"
-#include "prioq.h"
-#include "process-util.h"
-#include "set.h"
-#include "signal-util.h"
-#include "string-table.h"
-#include "string-util.h"
-#include "strxcpyx.h"
-#include "time-util.h"
-
-#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
-
-static bool EVENT_SOURCE_WATCH_PIDFD(sd_event_source *s) {
-        /* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
-        return s &&
-                s->type == SOURCE_CHILD &&
-                s->child.pidfd >= 0 &&
-                s->child.options == WEXITED;
-}
-
-static bool event_source_is_online(sd_event_source *s) {
-        assert(s);
-        return s->enabled != SD_EVENT_OFF && !s->ratelimited;
-}
-
-static bool event_source_is_offline(sd_event_source *s) {
-        assert(s);
-        return s->enabled == SD_EVENT_OFF || s->ratelimited;
-}
-
-static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
-        [SOURCE_IO] = "io",
-        [SOURCE_TIME_REALTIME] = "realtime",
-        [SOURCE_TIME_BOOTTIME] = "bootime",
-        [SOURCE_TIME_MONOTONIC] = "monotonic",
-        [SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm",
-        [SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm",
-        [SOURCE_SIGNAL] = "signal",
-        [SOURCE_CHILD] = "child",
-        [SOURCE_DEFER] = "defer",
-        [SOURCE_POST] = "post",
-        [SOURCE_EXIT] = "exit",
-        [SOURCE_WATCHDOG] = "watchdog",
-        [SOURCE_INOTIFY] = "inotify",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
-
-#define EVENT_SOURCE_IS_TIME(t)                 \
-        IN_SET((t),                             \
-               SOURCE_TIME_REALTIME,            \
-               SOURCE_TIME_BOOTTIME,            \
-               SOURCE_TIME_MONOTONIC,           \
-               SOURCE_TIME_REALTIME_ALARM,      \
-               SOURCE_TIME_BOOTTIME_ALARM)
-
-#define EVENT_SOURCE_CAN_RATE_LIMIT(t)          \
-        IN_SET((t),                             \
-               SOURCE_IO,                       \
-               SOURCE_TIME_REALTIME,            \
-               SOURCE_TIME_BOOTTIME,            \
-               SOURCE_TIME_MONOTONIC,           \
-               SOURCE_TIME_REALTIME_ALARM,      \
-               SOURCE_TIME_BOOTTIME_ALARM,      \
-               SOURCE_SIGNAL,                   \
-               SOURCE_DEFER,                    \
-               SOURCE_INOTIFY)
-
-struct sd_event {
-        unsigned n_ref;
-
-        int epoll_fd;
-        int watchdog_fd;
-
-        Prioq *pending;
-        Prioq *prepare;
-
-        /* timerfd_create() only supports these five clocks so far. We
-         * can add support for more clocks when the kernel learns to
-         * deal with them, too. */
-        struct clock_data realtime;
-        struct clock_data boottime;
-        struct clock_data monotonic;
-        struct clock_data realtime_alarm;
-        struct clock_data boottime_alarm;
-
-        usec_t perturb;
-
-        sd_event_source **signal_sources; /* indexed by signal number */
-        Hashmap *signal_data; /* indexed by priority */
-
-        Hashmap *child_sources;
-        unsigned n_online_child_sources;
-
-        Set *post_sources;
-
-        Prioq *exit;
-
-        Hashmap *inotify_data; /* indexed by priority */
-
-        /* A list of inode structures that still have an fd open, that we need to close before the next loop iteration */
-        LIST_HEAD(struct inode_data, inode_data_to_close);
-
-        /* A list of inotify objects that already have events buffered which aren't processed yet */
-        LIST_HEAD(struct inotify_data, inotify_data_buffered);
-
-        pid_t original_pid;
-
-        uint64_t iteration;
-        triple_timestamp timestamp;
-        int state;
-
-        bool exit_requested:1;
-        bool need_process_child:1;
-        bool watchdog:1;
-        bool profile_delays:1;
-
-        int exit_code;
-
-        pid_t tid;
-        sd_event **default_event_ptr;
-
-        usec_t watchdog_last, watchdog_period;
-
-        unsigned n_sources;
-
-        struct epoll_event *event_queue;
-        size_t event_queue_allocated;
-
-        LIST_HEAD(sd_event_source, sources);
-
-        usec_t last_run_usec, last_log_usec;
-        unsigned delays[sizeof(usec_t) * 8];
-};
-
-static thread_local sd_event *default_event = NULL;
-
-static void source_disconnect(sd_event_source *s);
-static void event_gc_inode_data(sd_event *e, struct inode_data *d);
-
-static sd_event *event_resolve(sd_event *e) {
-        return e == SD_EVENT_DEFAULT ? default_event : e;
-}
-
-static int pending_prioq_compare(const void *a, const void *b) {
-        const sd_event_source *x = a, *y = b;
-        int r;
-
-        assert(x->pending);
-        assert(y->pending);
-
-        /* Enabled ones first */
-        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
-                return -1;
-        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
-                return 1;
-
-        /* Non rate-limited ones first. */
-        r = CMP(!!x->ratelimited, !!y->ratelimited);
-        if (r != 0)
-                return r;
-
-        /* Lower priority values first */
-        r = CMP(x->priority, y->priority);
-        if (r != 0)
-                return r;
-
-        /* Older entries first */
-        return CMP(x->pending_iteration, y->pending_iteration);
-}
-
-static int prepare_prioq_compare(const void *a, const void *b) {
-        const sd_event_source *x = a, *y = b;
-        int r;
-
-        assert(x->prepare);
-        assert(y->prepare);
-
-        /* Enabled ones first */
-        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
-                return -1;
-        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
-                return 1;
-
-        /* Non rate-limited ones first. */
-        r = CMP(!!x->ratelimited, !!y->ratelimited);
-        if (r != 0)
-                return r;
-
-        /* Move most recently prepared ones last, so that we can stop
-         * preparing as soon as we hit one that has already been
-         * prepared in the current iteration */
-        r = CMP(x->prepare_iteration, y->prepare_iteration);
-        if (r != 0)
-                return r;
-
-        /* Lower priority values first */
-        return CMP(x->priority, y->priority);
-}
-
-static usec_t time_event_source_next(const sd_event_source *s) {
-        assert(s);
-
-        /* We have two kinds of event sources that have elapsation times associated with them: the actual
-         * time based ones and the ones for which a ratelimit can be in effect (where we want to be notified
-         * once the ratelimit time window ends). Let's return the next elapsing time depending on what we are
-         * looking at here. */
-
-        if (s->ratelimited) { /* If rate-limited the next elapsation is when the ratelimit time window ends */
-                assert(s->rate_limit.begin != 0);
-                assert(s->rate_limit.interval != 0);
-                return usec_add(s->rate_limit.begin, s->rate_limit.interval);
-        }
-
-        /* Otherwise this must be a time event source, if not ratelimited */
-        if (EVENT_SOURCE_IS_TIME(s->type))
-                return s->time.next;
-
-        return USEC_INFINITY;
-}
-
-static int earliest_time_prioq_compare(const void *a, const void *b) {
-        const sd_event_source *x = a, *y = b;
-
-        /* Enabled ones first */
-        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
-                return -1;
-        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
-                return 1;
-
-        /* Move the pending ones to the end */
-        if (!x->pending && y->pending)
-                return -1;
-        if (x->pending && !y->pending)
-                return 1;
-
-        /* Order by time */
-        return CMP(time_event_source_next(x), time_event_source_next(y));
-}
-
-static usec_t time_event_source_latest(const sd_event_source *s) {
-        assert(s);
-
-        if (s->ratelimited) { /* For ratelimited stuff the earliest and the latest time shall actually be the
-                               * same, as we should avoid adding additional inaccuracy on an inaccuracy time
-                               * window */
-                assert(s->rate_limit.begin != 0);
-                assert(s->rate_limit.interval != 0);
-                return usec_add(s->rate_limit.begin, s->rate_limit.interval);
-        }
-
-        /* Must be a time event source, if not ratelimited */
-        if (EVENT_SOURCE_IS_TIME(s->type))
-                return usec_add(s->time.next, s->time.accuracy);
-
-        return USEC_INFINITY;
-}
-
-static int latest_time_prioq_compare(const void *a, const void *b) {
-        const sd_event_source *x = a, *y = b;
-
-        /* Enabled ones first */
-        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
-                return -1;
-        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
-                return 1;
-
-        /* Move the pending ones to the end */
-        if (!x->pending && y->pending)
-                return -1;
-        if (x->pending && !y->pending)
-                return 1;
-
-        /* Order by time */
-        return CMP(time_event_source_latest(x), time_event_source_latest(y));
-}
-
-static int exit_prioq_compare(const void *a, const void *b) {
-        const sd_event_source *x = a, *y = b;
-
-        assert(x->type == SOURCE_EXIT);
-        assert(y->type == SOURCE_EXIT);
-
-        /* Enabled ones first */
-        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
-                return -1;
-        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
-                return 1;
-
-        /* Lower priority values first */
-        return CMP(x->priority, y->priority);
-}
-
-static void free_clock_data(struct clock_data *d) {
-        assert(d);
-        assert(d->wakeup == WAKEUP_CLOCK_DATA);
-
-        safe_close(d->fd);
-        prioq_free(d->earliest);
-        prioq_free(d->latest);
-}
-
-static sd_event *event_free(sd_event *e) {
-        sd_event_source *s;
-
-        assert(e);
-
-        while ((s = e->sources)) {
-                assert(s->floating);
-                source_disconnect(s);
-                sd_event_source_unref(s);
-        }
-
-        assert(e->n_sources == 0);
-
-        if (e->default_event_ptr)
-                *(e->default_event_ptr) = NULL;
-
-        safe_close(e->epoll_fd);
-        safe_close(e->watchdog_fd);
-
-        free_clock_data(&e->realtime);
-        free_clock_data(&e->boottime);
-        free_clock_data(&e->monotonic);
-        free_clock_data(&e->realtime_alarm);
-        free_clock_data(&e->boottime_alarm);
-
-        prioq_free(e->pending);
-        prioq_free(e->prepare);
-        prioq_free(e->exit);
-
-        free(e->signal_sources);
-        hashmap_free(e->signal_data);
-
-        hashmap_free(e->inotify_data);
-
-        hashmap_free(e->child_sources);
-        set_free(e->post_sources);
-
-        free(e->event_queue);
-
-        return mfree(e);
-}
-
-_public_ int sd_event_new(sd_event** ret) {
-        sd_event *e;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        e = new(sd_event, 1);
-        if (!e)
-                return -ENOMEM;
-
-        *e = (sd_event) {
-                .n_ref = 1,
-                .epoll_fd = -1,
-                .watchdog_fd = -1,
-                .realtime.wakeup = WAKEUP_CLOCK_DATA,
-                .realtime.fd = -1,
-                .realtime.next = USEC_INFINITY,
-                .boottime.wakeup = WAKEUP_CLOCK_DATA,
-                .boottime.fd = -1,
-                .boottime.next = USEC_INFINITY,
-                .monotonic.wakeup = WAKEUP_CLOCK_DATA,
-                .monotonic.fd = -1,
-                .monotonic.next = USEC_INFINITY,
-                .realtime_alarm.wakeup = WAKEUP_CLOCK_DATA,
-                .realtime_alarm.fd = -1,
-                .realtime_alarm.next = USEC_INFINITY,
-                .boottime_alarm.wakeup = WAKEUP_CLOCK_DATA,
-                .boottime_alarm.fd = -1,
-                .boottime_alarm.next = USEC_INFINITY,
-                .perturb = USEC_INFINITY,
-                .original_pid = getpid_cached(),
-        };
-
-        r = prioq_ensure_allocated(&e->pending, pending_prioq_compare);
-        if (r < 0)
-                goto fail;
-
-        e->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
-        if (e->epoll_fd < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        e->epoll_fd = fd_move_above_stdio(e->epoll_fd);
-
-        if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
-                log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
-                e->profile_delays = true;
-        }
-
-        *ret = e;
-        return 0;
-
-fail:
-        event_free(e);
-        return r;
-}
-
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free);
-
-_public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) {
-        if (s)
-                (void) sd_event_source_set_enabled(s, SD_EVENT_OFF);
-        return sd_event_source_unref(s);
-}
-
-static bool event_pid_changed(sd_event *e) {
-        assert(e);
-
-        /* We don't support people creating an event loop and keeping
-         * it around over a fork(). Let's complain. */
-
-        return e->original_pid != getpid_cached();
-}
-
-static void source_io_unregister(sd_event_source *s) {
-        assert(s);
-        assert(s->type == SOURCE_IO);
-
-        if (event_pid_changed(s->event))
-                return;
-
-        if (!s->io.registered)
-                return;
-
-        if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL) < 0)
-                log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll, ignoring: %m",
-                                strna(s->description), event_source_type_to_string(s->type));
-
-        s->io.registered = false;
-}
-
-static int source_io_register(
-                sd_event_source *s,
-                int enabled,
-                uint32_t events) {
-
-        assert(s);
-        assert(s->type == SOURCE_IO);
-        assert(enabled != SD_EVENT_OFF);
-
-        struct epoll_event ev = {
-                .events = events | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0),
-                .data.ptr = s,
-        };
-
-        if (epoll_ctl(s->event->epoll_fd,
-                      s->io.registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD,
-                      s->io.fd, &ev) < 0)
-                return -errno;
-
-        s->io.registered = true;
-
-        return 0;
-}
-
-static void source_child_pidfd_unregister(sd_event_source *s) {
-        assert(s);
-        assert(s->type == SOURCE_CHILD);
-
-        if (event_pid_changed(s->event))
-                return;
-
-        if (!s->child.registered)
-                return;
-
-        if (EVENT_SOURCE_WATCH_PIDFD(s))
-                if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->child.pidfd, NULL) < 0)
-                        log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll, ignoring: %m",
-                                        strna(s->description), event_source_type_to_string(s->type));
-
-        s->child.registered = false;
-}
-
-static int source_child_pidfd_register(sd_event_source *s, int enabled) {
-        assert(s);
-        assert(s->type == SOURCE_CHILD);
-        assert(enabled != SD_EVENT_OFF);
-
-        if (EVENT_SOURCE_WATCH_PIDFD(s)) {
-                struct epoll_event ev = {
-                        .events = EPOLLIN | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0),
-                        .data.ptr = s,
-                };
-
-                if (epoll_ctl(s->event->epoll_fd,
-                              s->child.registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD,
-                              s->child.pidfd, &ev) < 0)
-                        return -errno;
-        }
-
-        s->child.registered = true;
-        return 0;
-}
-
-static clockid_t event_source_type_to_clock(EventSourceType t) {
-
-        switch (t) {
-
-        case SOURCE_TIME_REALTIME:
-                return CLOCK_REALTIME;
-
-        case SOURCE_TIME_BOOTTIME:
-                return CLOCK_BOOTTIME;
-
-        case SOURCE_TIME_MONOTONIC:
-                return CLOCK_MONOTONIC;
-
-        case SOURCE_TIME_REALTIME_ALARM:
-                return CLOCK_REALTIME_ALARM;
-
-        case SOURCE_TIME_BOOTTIME_ALARM:
-                return CLOCK_BOOTTIME_ALARM;
-
-        default:
-                return (clockid_t) -1;
-        }
-}
-
-static EventSourceType clock_to_event_source_type(clockid_t clock) {
-
-        switch (clock) {
-
-        case CLOCK_REALTIME:
-                return SOURCE_TIME_REALTIME;
-
-        case CLOCK_BOOTTIME:
-                return SOURCE_TIME_BOOTTIME;
-
-        case CLOCK_MONOTONIC:
-                return SOURCE_TIME_MONOTONIC;
-
-        case CLOCK_REALTIME_ALARM:
-                return SOURCE_TIME_REALTIME_ALARM;
-
-        case CLOCK_BOOTTIME_ALARM:
-                return SOURCE_TIME_BOOTTIME_ALARM;
-
-        default:
-                return _SOURCE_EVENT_SOURCE_TYPE_INVALID;
-        }
-}
-
-static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) {
-        assert(e);
-
-        switch (t) {
-
-        case SOURCE_TIME_REALTIME:
-                return &e->realtime;
-
-        case SOURCE_TIME_BOOTTIME:
-                return &e->boottime;
-
-        case SOURCE_TIME_MONOTONIC:
-                return &e->monotonic;
-
-        case SOURCE_TIME_REALTIME_ALARM:
-                return &e->realtime_alarm;
-
-        case SOURCE_TIME_BOOTTIME_ALARM:
-                return &e->boottime_alarm;
-
-        default:
-                return NULL;
-        }
-}
-
-static void event_free_signal_data(sd_event *e, struct signal_data *d) {
-        assert(e);
-
-        if (!d)
-                return;
-
-        hashmap_remove(e->signal_data, &d->priority);
-        safe_close(d->fd);
-        free(d);
-}
-
-static int event_make_signal_data(
-                sd_event *e,
-                int sig,
-                struct signal_data **ret) {
-
-        struct signal_data *d;
-        bool added = false;
-        sigset_t ss_copy;
-        int64_t priority;
-        int r;
-
-        assert(e);
-
-        if (event_pid_changed(e))
-                return -ECHILD;
-
-        if (e->signal_sources && e->signal_sources[sig])
-                priority = e->signal_sources[sig]->priority;
-        else
-                priority = SD_EVENT_PRIORITY_NORMAL;
-
-        d = hashmap_get(e->signal_data, &priority);
-        if (d) {
-                if (sigismember(&d->sigset, sig) > 0) {
-                        if (ret)
-                                *ret = d;
-                        return 0;
-                }
-        } else {
-                r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops);
-                if (r < 0)
-                        return r;
-
-                d = new(struct signal_data, 1);
-                if (!d)
-                        return -ENOMEM;
-
-                *d = (struct signal_data) {
-                        .wakeup = WAKEUP_SIGNAL_DATA,
-                        .fd = -1,
-                        .priority = priority,
-                };
-
-                r = hashmap_put(e->signal_data, &d->priority, d);
-                if (r < 0) {
-                        free(d);
-                        return r;
-                }
-
-                added = true;
-        }
-
-        ss_copy = d->sigset;
-        assert_se(sigaddset(&ss_copy, sig) >= 0);
-
-        r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC);
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        d->sigset = ss_copy;
-
-        if (d->fd >= 0) {
-                if (ret)
-                        *ret = d;
-                return 0;
-        }
-
-        d->fd = fd_move_above_stdio(r);
-
-        struct epoll_event ev = {
-                .events = EPOLLIN,
-                .data.ptr = d,
-        };
-
-        if (epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev) < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        if (ret)
-                *ret = d;
-
-        return 0;
-
-fail:
-        if (added)
-                event_free_signal_data(e, d);
-
-        return r;
-}
-
-static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) {
-        assert(e);
-        assert(d);
-
-        /* Turns off the specified signal in the signal data
-         * object. If the signal mask of the object becomes empty that
-         * way removes it. */
-
-        if (sigismember(&d->sigset, sig) == 0)
-                return;
-
-        assert_se(sigdelset(&d->sigset, sig) >= 0);
-
-        if (sigisemptyset(&d->sigset)) {
-                /* If all the mask is all-zero we can get rid of the structure */
-                event_free_signal_data(e, d);
-                return;
-        }
-
-        assert(d->fd >= 0);
-
-        if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0)
-                log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m");
-}
-
-static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) {
-        struct signal_data *d;
-        static const int64_t zero_priority = 0;
-
-        assert(e);
-
-        /* Rechecks if the specified signal is still something we are interested in. If not, we'll unmask it,
-         * and possibly drop the signalfd for it. */
-
-        if (sig == SIGCHLD &&
-            e->n_online_child_sources > 0)
-                return;
-
-        if (e->signal_sources &&
-            e->signal_sources[sig] &&
-            event_source_is_online(e->signal_sources[sig]))
-                return;
-
-        /*
-         * The specified signal might be enabled in three different queues:
-         *
-         * 1) the one that belongs to the priority passed (if it is non-NULL)
-         * 2) the one that belongs to the priority of the event source of the signal (if there is one)
-         * 3) the 0 priority (to cover the SIGCHLD case)
-         *
-         * Hence, let's remove it from all three here.
-         */
-
-        if (priority) {
-                d = hashmap_get(e->signal_data, priority);
-                if (d)
-                        event_unmask_signal_data(e, d, sig);
-        }
-
-        if (e->signal_sources && e->signal_sources[sig]) {
-                d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority);
-                if (d)
-                        event_unmask_signal_data(e, d, sig);
-        }
-
-        d = hashmap_get(e->signal_data, &zero_priority);
-        if (d)
-                event_unmask_signal_data(e, d, sig);
-}
-
-static void event_source_pp_prioq_reshuffle(sd_event_source *s) {
-        assert(s);
-
-        /* Reshuffles the pending + prepare prioqs. Called whenever the dispatch order changes, i.e. when
-         * they are enabled/disabled or marked pending and such. */
-
-        if (s->pending)
-                prioq_reshuffle(s->event->pending, s, &s->pending_index);
-
-        if (s->prepare)
-                prioq_reshuffle(s->event->prepare, s, &s->prepare_index);
-}
-
-static void event_source_time_prioq_reshuffle(sd_event_source *s) {
-        struct clock_data *d;
-
-        assert(s);
-
-        /* Called whenever the event source's timer ordering properties changed, i.e. time, accuracy,
-         * pending, enable state. Makes sure the two prioq's are ordered properly again. */
-
-        if (s->ratelimited)
-                d = &s->event->monotonic;
-        else {
-                assert(EVENT_SOURCE_IS_TIME(s->type));
-                assert_se(d = event_get_clock_data(s->event, s->type));
-        }
-
-        prioq_reshuffle(d->earliest, s, &s->earliest_index);
-        prioq_reshuffle(d->latest, s, &s->latest_index);
-        d->needs_rearm = true;
-}
-
-static void event_source_time_prioq_remove(
-                sd_event_source *s,
-                struct clock_data *d) {
-
-        assert(s);
-        assert(d);
-
-        prioq_remove(d->earliest, s, &s->earliest_index);
-        prioq_remove(d->latest, s, &s->latest_index);
-        s->earliest_index = s->latest_index = PRIOQ_IDX_NULL;
-        d->needs_rearm = true;
-}
-
-static void source_disconnect(sd_event_source *s) {
-        sd_event *event;
-
-        assert(s);
-
-        if (!s->event)
-                return;
-
-        assert(s->event->n_sources > 0);
-
-        switch (s->type) {
-
-        case SOURCE_IO:
-                if (s->io.fd >= 0)
-                        source_io_unregister(s);
-
-                break;
-
-        case SOURCE_TIME_REALTIME:
-        case SOURCE_TIME_BOOTTIME:
-        case SOURCE_TIME_MONOTONIC:
-        case SOURCE_TIME_REALTIME_ALARM:
-        case SOURCE_TIME_BOOTTIME_ALARM:
-                /* Only remove this event source from the time event source here if it is not ratelimited. If
-                 * it is ratelimited, we'll remove it below, separately. Why? Because the clock used might
-                 * differ: ratelimiting always uses CLOCK_MONOTONIC, but timer events might use any clock */
-
-                if (!s->ratelimited) {
-                        struct clock_data *d;
-                        assert_se(d = event_get_clock_data(s->event, s->type));
-                        event_source_time_prioq_remove(s, d);
-                }
-
-                break;
-
-        case SOURCE_SIGNAL:
-                if (s->signal.sig > 0) {
-
-                        if (s->event->signal_sources)
-                                s->event->signal_sources[s->signal.sig] = NULL;
-
-                        event_gc_signal_data(s->event, &s->priority, s->signal.sig);
-                }
-
-                break;
-
-        case SOURCE_CHILD:
-                if (s->child.pid > 0) {
-                        if (event_source_is_online(s)) {
-                                assert(s->event->n_online_child_sources > 0);
-                                s->event->n_online_child_sources--;
-                        }
-
-                        (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
-                }
-
-                if (EVENT_SOURCE_WATCH_PIDFD(s))
-                        source_child_pidfd_unregister(s);
-                else
-                        event_gc_signal_data(s->event, &s->priority, SIGCHLD);
-
-                break;
-
-        case SOURCE_DEFER:
-                /* nothing */
-                break;
-
-        case SOURCE_POST:
-                set_remove(s->event->post_sources, s);
-                break;
-
-        case SOURCE_EXIT:
-                prioq_remove(s->event->exit, s, &s->exit.prioq_index);
-                break;
-
-        case SOURCE_INOTIFY: {
-                struct inode_data *inode_data;
-
-                inode_data = s->inotify.inode_data;
-                if (inode_data) {
-                        struct inotify_data *inotify_data;
-                        assert_se(inotify_data = inode_data->inotify_data);
-
-                        /* Detach this event source from the inode object */
-                        LIST_REMOVE(inotify.by_inode_data, inode_data->event_sources, s);
-                        s->inotify.inode_data = NULL;
-
-                        if (s->pending) {
-                                assert(inotify_data->n_pending > 0);
-                                inotify_data->n_pending--;
-                        }
-
-                        /* Note that we don't reduce the inotify mask for the watch descriptor here if the inode is
-                         * continued to being watched. That's because inotify doesn't really have an API for that: we
-                         * can only change watch masks with access to the original inode either by fd or by path. But
-                         * paths aren't stable, and keeping an O_PATH fd open all the time would mean wasting an fd
-                         * continuously and keeping the mount busy which we can't really do. We could reconstruct the
-                         * original inode from /proc/self/fdinfo/$INOTIFY_FD (as all watch descriptors are listed
-                         * there), but given the need for open_by_handle_at() which is privileged and not universally
-                         * available this would be quite an incomplete solution. Hence we go the other way, leave the
-                         * mask set, even if it is not minimized now, and ignore all events we aren't interested in
-                         * anymore after reception. Yes, this sucks, but … Linux … */
-
-                        /* Maybe release the inode data (and its inotify) */
-                        event_gc_inode_data(s->event, inode_data);
-                }
-
-                break;
-        }
-
-        default:
-                assert_not_reached("Wut? I shouldn't exist.");
-        }
-
-        if (s->pending)
-                prioq_remove(s->event->pending, s, &s->pending_index);
-
-        if (s->prepare)
-                prioq_remove(s->event->prepare, s, &s->prepare_index);
-
-        if (s->ratelimited)
-                event_source_time_prioq_remove(s, &s->event->monotonic);
-
-        event = TAKE_PTR(s->event);
-        LIST_REMOVE(sources, event->sources, s);
-        event->n_sources--;
-
-        /* Note that we don't invalidate the type here, since we still need it in order to close the fd or
-         * pidfd associated with this event source, which we'll do only on source_free(). */
-
-        if (!s->floating)
-                sd_event_unref(event);
-}
-
-static void source_free(sd_event_source *s) {
-        assert(s);
-
-        source_disconnect(s);
-
-        if (s->type == SOURCE_IO && s->io.owned)
-                s->io.fd = safe_close(s->io.fd);
-
-        if (s->type == SOURCE_CHILD) {
-                /* Eventually the kernel will do this automatically for us, but for now let's emulate this (unreliably) in userspace. */
-
-                if (s->child.process_owned) {
-
-                        if (!s->child.exited) {
-                                bool sent = false;
-
-                                if (s->child.pidfd >= 0) {
-                                        if (pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0) < 0) {
-                                                if (errno == ESRCH) /* Already dead */
-                                                        sent = true;
-                                                else if (!ERRNO_IS_NOT_SUPPORTED(errno))
-                                                        log_debug_errno(errno, "Failed to kill process " PID_FMT " via pidfd_send_signal(), re-trying via kill(): %m",
-                                                                        s->child.pid);
-                                        } else
-                                                sent = true;
-                                }
-
-                                if (!sent)
-                                        if (kill(s->child.pid, SIGKILL) < 0)
-                                                if (errno != ESRCH) /* Already dead */
-                                                        log_debug_errno(errno, "Failed to kill process " PID_FMT " via kill(), ignoring: %m",
-                                                                        s->child.pid);
-                        }
-
-                        if (!s->child.waited) {
-                                siginfo_t si = {};
-
-                                /* Reap the child if we can */
-                                (void) waitid(P_PID, s->child.pid, &si, WEXITED);
-                        }
-                }
-
-                if (s->child.pidfd_owned)
-                        s->child.pidfd = safe_close(s->child.pidfd);
-        }
-
-        if (s->destroy_callback)
-                s->destroy_callback(s->userdata);
-
-        free(s->description);
-        free(s);
-}
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, source_free);
-
-static int source_set_pending(sd_event_source *s, bool b) {
-        int r;
-
-        assert(s);
-        assert(s->type != SOURCE_EXIT);
-
-        if (s->pending == b)
-                return 0;
-
-        s->pending = b;
-
-        if (b) {
-                s->pending_iteration = s->event->iteration;
-
-                r = prioq_put(s->event->pending, s, &s->pending_index);
-                if (r < 0) {
-                        s->pending = false;
-                        return r;
-                }
-        } else
-                assert_se(prioq_remove(s->event->pending, s, &s->pending_index));
-
-        if (EVENT_SOURCE_IS_TIME(s->type))
-                event_source_time_prioq_reshuffle(s);
-
-        if (s->type == SOURCE_SIGNAL && !b) {
-                struct signal_data *d;
-
-                d = hashmap_get(s->event->signal_data, &s->priority);
-                if (d && d->current == s)
-                        d->current = NULL;
-        }
-
-        if (s->type == SOURCE_INOTIFY) {
-
-                assert(s->inotify.inode_data);
-                assert(s->inotify.inode_data->inotify_data);
-
-                if (b)
-                        s->inotify.inode_data->inotify_data->n_pending ++;
-                else {
-                        assert(s->inotify.inode_data->inotify_data->n_pending > 0);
-                        s->inotify.inode_data->inotify_data->n_pending --;
-                }
-        }
-
-        return 0;
-}
-
-static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) {
-        sd_event_source *s;
-
-        assert(e);
-
-        s = new(sd_event_source, 1);
-        if (!s)
-                return NULL;
-
-        *s = (struct sd_event_source) {
-                .n_ref = 1,
-                .event = e,
-                .floating = floating,
-                .type = type,
-                .pending_index = PRIOQ_IDX_NULL,
-                .prepare_index = PRIOQ_IDX_NULL,
-        };
-
-        if (!floating)
-                sd_event_ref(e);
-
-        LIST_PREPEND(sources, e->sources, s);
-        e->n_sources++;
-
-        return s;
-}
-
-static int io_exit_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        assert(s);
-
-        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
-}
-
-_public_ int sd_event_add_io(
-                sd_event *e,
-                sd_event_source **ret,
-                int fd,
-                uint32_t events,
-                sd_event_io_handler_t callback,
-                void *userdata) {
-
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(fd >= 0, -EBADF);
-        assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!callback)
-                callback = io_exit_callback;
-
-        s = source_new(e, !ret, SOURCE_IO);
-        if (!s)
-                return -ENOMEM;
-
-        s->wakeup = WAKEUP_EVENT_SOURCE;
-        s->io.fd = fd;
-        s->io.events = events;
-        s->io.callback = callback;
-        s->userdata = userdata;
-        s->enabled = SD_EVENT_ON;
-
-        r = source_io_register(s, s->enabled, events);
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-
-        return 0;
-}
-
-static void initialize_perturb(sd_event *e) {
-        sd_id128_t bootid = {};
-
-        /* When we sleep for longer, we try to realign the wakeup to
-           the same time within each minute/second/250ms, so that
-           events all across the system can be coalesced into a single
-           CPU wakeup. However, let's take some system-specific
-           randomness for this value, so that in a network of systems
-           with synced clocks timer events are distributed a
-           bit. Here, we calculate a perturbation usec offset from the
-           boot ID. */
-
-        if (_likely_(e->perturb != USEC_INFINITY))
-                return;
-
-        if (sd_id128_get_boot(&bootid) >= 0)
-                e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE;
-}
-
-static int event_setup_timer_fd(
-                sd_event *e,
-                struct clock_data *d,
-                clockid_t clock) {
-
-        assert(e);
-        assert(d);
-
-        if (_likely_(d->fd >= 0))
-                return 0;
-
-        _cleanup_close_ int fd = -1;
-
-        fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        fd = fd_move_above_stdio(fd);
-
-        struct epoll_event ev = {
-                .events = EPOLLIN,
-                .data.ptr = d,
-        };
-
-        if (epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
-                return -errno;
-
-        d->fd = TAKE_FD(fd);
-        return 0;
-}
-
-static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) {
-        assert(s);
-
-        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
-}
-
-static int setup_clock_data(sd_event *e, struct clock_data *d, clockid_t clock) {
-        int r;
-
-        assert(d);
-
-        if (d->fd < 0) {
-                r = event_setup_timer_fd(e, d, clock);
-                if (r < 0)
-                        return r;
-        }
-
-        r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare);
-        if (r < 0)
-                return r;
-
-        r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
-static int event_source_time_prioq_put(
-                sd_event_source *s,
-                struct clock_data *d) {
-
-        int r;
-
-        assert(s);
-        assert(d);
-
-        r = prioq_put(d->earliest, s, &s->earliest_index);
-        if (r < 0)
-                return r;
-
-        r = prioq_put(d->latest, s, &s->latest_index);
-        if (r < 0) {
-                assert_se(prioq_remove(d->earliest, s, &s->earliest_index) > 0);
-                s->earliest_index = PRIOQ_IDX_NULL;
-                return r;
-        }
-
-        d->needs_rearm = true;
-        return 0;
-}
-
-_public_ int sd_event_add_time(
-                sd_event *e,
-                sd_event_source **ret,
-                clockid_t clock,
-                uint64_t usec,
-                uint64_t accuracy,
-                sd_event_time_handler_t callback,
-                void *userdata) {
-
-        EventSourceType type;
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        struct clock_data *d;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(accuracy != (uint64_t) -1, -EINVAL);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!clock_supported(clock)) /* Checks whether the kernel supports the clock */
-                return -EOPNOTSUPP;
-
-        type = clock_to_event_source_type(clock); /* checks whether sd-event supports this clock */
-        if (type < 0)
-                return -EOPNOTSUPP;
-
-        if (!callback)
-                callback = time_exit_callback;
-
-        assert_se(d = event_get_clock_data(e, type));
-
-        r = setup_clock_data(e, d, clock);
-        if (r < 0)
-                return r;
-
-        s = source_new(e, !ret, type);
-        if (!s)
-                return -ENOMEM;
-
-        s->time.next = usec;
-        s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy;
-        s->time.callback = callback;
-        s->earliest_index = s->latest_index = PRIOQ_IDX_NULL;
-        s->userdata = userdata;
-        s->enabled = SD_EVENT_ONESHOT;
-
-        r = event_source_time_prioq_put(s, d);
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-
-        return 0;
-}
-
-#if 0 /* NM_IGNORED */
-_public_ int sd_event_add_time_relative(
-                sd_event *e,
-                sd_event_source **ret,
-                clockid_t clock,
-                uint64_t usec,
-                uint64_t accuracy,
-                sd_event_time_handler_t callback,
-                void *userdata) {
-
-        usec_t t;
-        int r;
-
-        /* Same as sd_event_add_time() but operates relative to the event loop's current point in time, and
-         * checks for overflow. */
-
-        r = sd_event_now(e, clock, &t);
-        if (r < 0)
-                return r;
-
-        if (usec >= USEC_INFINITY - t)
-                return -EOVERFLOW;
-
-        return sd_event_add_time(e, ret, clock, t + usec, accuracy, callback, userdata);
-}
-
-static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
-        assert(s);
-
-        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
-}
-
-_public_ int sd_event_add_signal(
-                sd_event *e,
-                sd_event_source **ret,
-                int sig,
-                sd_event_signal_handler_t callback,
-                void *userdata) {
-
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        struct signal_data *d;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(SIGNAL_VALID(sig), -EINVAL);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!callback)
-                callback = signal_exit_callback;
-
-        r = signal_is_blocked(sig);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -EBUSY;
-
-        if (!e->signal_sources) {
-                e->signal_sources = new0(sd_event_source*, _NSIG);
-                if (!e->signal_sources)
-                        return -ENOMEM;
-        } else if (e->signal_sources[sig])
-                return -EBUSY;
-
-        s = source_new(e, !ret, SOURCE_SIGNAL);
-        if (!s)
-                return -ENOMEM;
-
-        s->signal.sig = sig;
-        s->signal.callback = callback;
-        s->userdata = userdata;
-        s->enabled = SD_EVENT_ON;
-
-        e->signal_sources[sig] = s;
-
-        r = event_make_signal_data(e, sig, &d);
-        if (r < 0)
-                return r;
-
-        /* Use the signal name as description for the event source by default */
-        (void) sd_event_source_set_description(s, signal_to_string(sig));
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-
-        return 0;
-}
-
-static int child_exit_callback(sd_event_source *s, const siginfo_t *si, void *userdata) {
-        assert(s);
-
-        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
-}
-
-static bool shall_use_pidfd(void) {
-        /* Mostly relevant for debugging, i.e. this is used in test-event.c to test the event loop once with and once without pidfd */
-        return getenv_bool_secure("SYSTEMD_PIDFD") != 0;
-}
-
-_public_ int sd_event_add_child(
-                sd_event *e,
-                sd_event_source **ret,
-                pid_t pid,
-                int options,
-                sd_event_child_handler_t callback,
-                void *userdata) {
-
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(pid > 1, -EINVAL);
-        assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
-        assert_return(options != 0, -EINVAL);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!callback)
-                callback = child_exit_callback;
-
-        if (e->n_online_child_sources == 0) {
-                /* Caller must block SIGCHLD before using us to watch children, even if pidfd is available,
-                 * for compatibility with pre-pidfd and because we don't want the reap the child processes
-                 * ourselves, i.e. call waitid(), and don't want Linux' default internal logic for that to
-                 * take effect.
-                 *
-                 * (As an optimization we only do this check on the first child event source created.) */
-                r = signal_is_blocked(SIGCHLD);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        return -EBUSY;
-        }
-
-        r = hashmap_ensure_allocated(&e->child_sources, NULL);
-        if (r < 0)
-                return r;
-
-        if (hashmap_contains(e->child_sources, PID_TO_PTR(pid)))
-                return -EBUSY;
-
-        s = source_new(e, !ret, SOURCE_CHILD);
-        if (!s)
-                return -ENOMEM;
-
-        s->wakeup = WAKEUP_EVENT_SOURCE;
-        s->child.pid = pid;
-        s->child.options = options;
-        s->child.callback = callback;
-        s->userdata = userdata;
-        s->enabled = SD_EVENT_ONESHOT;
-
-        /* We always take a pidfd here if we can, even if we wait for anything else than WEXITED, so that we
-         * pin the PID, and make regular waitid() handling race-free. */
-
-        if (shall_use_pidfd()) {
-                s->child.pidfd = pidfd_open(s->child.pid, 0);
-                if (s->child.pidfd < 0) {
-                        /* Propagate errors unless the syscall is not supported or blocked */
-                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
-                                return -errno;
-                } else
-                        s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
-        } else
-                s->child.pidfd = -1;
-
-        r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
-        if (r < 0)
-                return r;
-
-        if (EVENT_SOURCE_WATCH_PIDFD(s)) {
-                /* We have a pidfd and we only want to watch for exit */
-                r = source_child_pidfd_register(s, s->enabled);
-                if (r < 0)
-                        return r;
-
-        } else {
-                /* We have no pidfd or we shall wait for some other event than WEXITED */
-                r = event_make_signal_data(e, SIGCHLD, NULL);
-                if (r < 0)
-                        return r;
-
-                e->need_process_child = true;
-        }
-
-        e->n_online_child_sources++;
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-        return 0;
-}
-
-_public_ int sd_event_add_child_pidfd(
-                sd_event *e,
-                sd_event_source **ret,
-                int pidfd,
-                int options,
-                sd_event_child_handler_t callback,
-                void *userdata) {
-
-
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        pid_t pid;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(pidfd >= 0, -EBADF);
-        assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
-        assert_return(options != 0, -EINVAL);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!callback)
-                callback = child_exit_callback;
-
-        if (e->n_online_child_sources == 0) {
-                r = signal_is_blocked(SIGCHLD);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        return -EBUSY;
-        }
-
-        r = hashmap_ensure_allocated(&e->child_sources, NULL);
-        if (r < 0)
-                return r;
-
-        r = pidfd_get_pid(pidfd, &pid);
-        if (r < 0)
-                return r;
-
-        if (hashmap_contains(e->child_sources, PID_TO_PTR(pid)))
-                return -EBUSY;
-
-        s = source_new(e, !ret, SOURCE_CHILD);
-        if (!s)
-                return -ENOMEM;
-
-        s->wakeup = WAKEUP_EVENT_SOURCE;
-        s->child.pidfd = pidfd;
-        s->child.pid = pid;
-        s->child.options = options;
-        s->child.callback = callback;
-        s->child.pidfd_owned = false; /* If we got the pidfd passed in we don't own it by default (similar to the IO fd case) */
-        s->userdata = userdata;
-        s->enabled = SD_EVENT_ONESHOT;
-
-        r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
-        if (r < 0)
-                return r;
-
-        if (EVENT_SOURCE_WATCH_PIDFD(s)) {
-                /* We only want to watch for WEXITED */
-                r = source_child_pidfd_register(s, s->enabled);
-                if (r < 0)
-                        return r;
-        } else {
-                /* We shall wait for some other event than WEXITED */
-                r = event_make_signal_data(e, SIGCHLD, NULL);
-                if (r < 0)
-                        return r;
-
-                e->need_process_child = true;
-        }
-
-        e->n_online_child_sources++;
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-        return 0;
-}
-#endif /* NM_IGNORED */
-
-static int generic_exit_callback(sd_event_source *s, void *userdata) {
-        assert(s);
-
-        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
-}
-
-_public_ int sd_event_add_defer(
-                sd_event *e,
-                sd_event_source **ret,
-                sd_event_handler_t callback,
-                void *userdata) {
-
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!callback)
-                callback = generic_exit_callback;
-
-        s = source_new(e, !ret, SOURCE_DEFER);
-        if (!s)
-                return -ENOMEM;
-
-        s->defer.callback = callback;
-        s->userdata = userdata;
-        s->enabled = SD_EVENT_ONESHOT;
-
-        r = source_set_pending(s, true);
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-
-        return 0;
-}
-
-_public_ int sd_event_add_post(
-                sd_event *e,
-                sd_event_source **ret,
-                sd_event_handler_t callback,
-                void *userdata) {
-
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!callback)
-                callback = generic_exit_callback;
-
-        s = source_new(e, !ret, SOURCE_POST);
-        if (!s)
-                return -ENOMEM;
-
-        s->post.callback = callback;
-        s->userdata = userdata;
-        s->enabled = SD_EVENT_ON;
-
-        r = set_ensure_put(&e->post_sources, NULL, s);
-        if (r < 0)
-                return r;
-        assert(r > 0);
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-
-        return 0;
-}
-
-_public_ int sd_event_add_exit(
-                sd_event *e,
-                sd_event_source **ret,
-                sd_event_handler_t callback,
-                void *userdata) {
-
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(callback, -EINVAL);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        r = prioq_ensure_allocated(&e->exit, exit_prioq_compare);
-        if (r < 0)
-                return r;
-
-        s = source_new(e, !ret, SOURCE_EXIT);
-        if (!s)
-                return -ENOMEM;
-
-        s->exit.callback = callback;
-        s->userdata = userdata;
-        s->exit.prioq_index = PRIOQ_IDX_NULL;
-        s->enabled = SD_EVENT_ONESHOT;
-
-        r = prioq_put(s->event->exit, s, &s->exit.prioq_index);
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-
-        return 0;
-}
-
-static void event_free_inotify_data(sd_event *e, struct inotify_data *d) {
-        assert(e);
-
-        if (!d)
-                return;
-
-        assert(hashmap_isempty(d->inodes));
-        assert(hashmap_isempty(d->wd));
-
-        if (d->buffer_filled > 0)
-                LIST_REMOVE(buffered, e->inotify_data_buffered, d);
-
-        hashmap_free(d->inodes);
-        hashmap_free(d->wd);
-
-        assert_se(hashmap_remove(e->inotify_data, &d->priority) == d);
-
-        if (d->fd >= 0) {
-                if (epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, d->fd, NULL) < 0)
-                        log_debug_errno(errno, "Failed to remove inotify fd from epoll, ignoring: %m");
-
-                safe_close(d->fd);
-        }
-        free(d);
-}
-
-static int event_make_inotify_data(
-                sd_event *e,
-                int64_t priority,
-                struct inotify_data **ret) {
-
-        _cleanup_close_ int fd = -1;
-        struct inotify_data *d;
-        int r;
-
-        assert(e);
-
-        d = hashmap_get(e->inotify_data, &priority);
-        if (d) {
-                if (ret)
-                        *ret = d;
-                return 0;
-        }
-
-        fd = inotify_init1(IN_NONBLOCK|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        fd = fd_move_above_stdio(fd);
-
-        r = hashmap_ensure_allocated(&e->inotify_data, &uint64_hash_ops);
-        if (r < 0)
-                return r;
-
-        d = new(struct inotify_data, 1);
-        if (!d)
-                return -ENOMEM;
-
-        *d = (struct inotify_data) {
-                .wakeup = WAKEUP_INOTIFY_DATA,
-                .fd = TAKE_FD(fd),
-                .priority = priority,
-        };
-
-        r = hashmap_put(e->inotify_data, &d->priority, d);
-        if (r < 0) {
-                d->fd = safe_close(d->fd);
-                free(d);
-                return r;
-        }
-
-        struct epoll_event ev = {
-                .events = EPOLLIN,
-                .data.ptr = d,
-        };
-
-        if (epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev) < 0) {
-                r = -errno;
-                d->fd = safe_close(d->fd); /* let's close this ourselves, as event_free_inotify_data() would otherwise
-                                            * remove the fd from the epoll first, which we don't want as we couldn't
-                                            * add it in the first place. */
-                event_free_inotify_data(e, d);
-                return r;
-        }
-
-        if (ret)
-                *ret = d;
-
-        return 1;
-}
-
-static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) {
-        int r;
-
-        assert(x);
-        assert(y);
-
-        r = CMP(x->dev, y->dev);
-        if (r != 0)
-                return r;
-
-        return CMP(x->ino, y->ino);
-}
-
-static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
-        assert(d);
-
-        siphash24_compress(&d->dev, sizeof(d->dev), state);
-        siphash24_compress(&d->ino, sizeof(d->ino), state);
-}
-
-DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
-
-static void event_free_inode_data(
-                sd_event *e,
-                struct inode_data *d) {
-
-        assert(e);
-
-        if (!d)
-                return;
-
-        assert(!d->event_sources);
-
-        if (d->fd >= 0) {
-                LIST_REMOVE(to_close, e->inode_data_to_close, d);
-                safe_close(d->fd);
-        }
-
-        if (d->inotify_data) {
-
-                if (d->wd >= 0) {
-                        if (d->inotify_data->fd >= 0) {
-                                /* So here's a problem. At the time this runs the watch descriptor might already be
-                                 * invalidated, because an IN_IGNORED event might be queued right the moment we enter
-                                 * the syscall. Hence, whenever we get EINVAL, ignore it entirely, since it's a very
-                                 * likely case to happen. */
-
-                                if (inotify_rm_watch(d->inotify_data->fd, d->wd) < 0 && errno != EINVAL)
-                                        log_debug_errno(errno, "Failed to remove watch descriptor %i from inotify, ignoring: %m", d->wd);
-                        }
-
-                        assert_se(hashmap_remove(d->inotify_data->wd, INT_TO_PTR(d->wd)) == d);
-                }
-
-                assert_se(hashmap_remove(d->inotify_data->inodes, d) == d);
-        }
-
-        free(d);
-}
-
-static void event_gc_inode_data(
-                sd_event *e,
-                struct inode_data *d) {
-
-        struct inotify_data *inotify_data;
-
-        assert(e);
-
-        if (!d)
-                return;
-
-        if (d->event_sources)
-                return;
-
-        inotify_data = d->inotify_data;
-        event_free_inode_data(e, d);
-
-        if (inotify_data && hashmap_isempty(inotify_data->inodes))
-                event_free_inotify_data(e, inotify_data);
-}
-
-static int event_make_inode_data(
-                sd_event *e,
-                struct inotify_data *inotify_data,
-                dev_t dev,
-                ino_t ino,
-                struct inode_data **ret) {
-
-        struct inode_data *d, key;
-        int r;
-
-        assert(e);
-        assert(inotify_data);
-
-        key = (struct inode_data) {
-                .ino = ino,
-                .dev = dev,
-        };
-
-        d = hashmap_get(inotify_data->inodes, &key);
-        if (d) {
-                if (ret)
-                        *ret = d;
-
-                return 0;
-        }
-
-        r = hashmap_ensure_allocated(&inotify_data->inodes, &inode_data_hash_ops);
-        if (r < 0)
-                return r;
-
-        d = new(struct inode_data, 1);
-        if (!d)
-                return -ENOMEM;
-
-        *d = (struct inode_data) {
-                .dev = dev,
-                .ino = ino,
-                .wd = -1,
-                .fd = -1,
-                .inotify_data = inotify_data,
-        };
-
-        r = hashmap_put(inotify_data->inodes, d, d);
-        if (r < 0) {
-                free(d);
-                return r;
-        }
-
-        if (ret)
-                *ret = d;
-
-        return 1;
-}
-
-static uint32_t inode_data_determine_mask(struct inode_data *d) {
-        bool excl_unlink = true;
-        uint32_t combined = 0;
-        sd_event_source *s;
-
-        assert(d);
-
-        /* Combines the watch masks of all event sources watching this inode. We generally just OR them together, but
-         * the IN_EXCL_UNLINK flag is ANDed instead.
-         *
-         * Note that we add all sources to the mask here, regardless whether enabled, disabled or oneshot. That's
-         * because we cannot change the mask anymore after the event source was created once, since the kernel has no
-         * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and suppress
-         * events we don't care for client-side. */
-
-        LIST_FOREACH(inotify.by_inode_data, s, d->event_sources) {
-
-                if ((s->inotify.mask & IN_EXCL_UNLINK) == 0)
-                        excl_unlink = false;
-
-                combined |= s->inotify.mask;
-        }
-
-        return (combined & ~(IN_ONESHOT|IN_DONT_FOLLOW|IN_ONLYDIR|IN_EXCL_UNLINK)) | (excl_unlink ? IN_EXCL_UNLINK : 0);
-}
-
-static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
-        uint32_t combined_mask;
-        int wd, r;
-
-        assert(d);
-        assert(d->fd >= 0);
-
-        combined_mask = inode_data_determine_mask(d);
-
-        if (d->wd >= 0 && combined_mask == d->combined_mask)
-                return 0;
-
-        r = hashmap_ensure_allocated(&d->inotify_data->wd, NULL);
-        if (r < 0)
-                return r;
-
-        wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
-        if (wd < 0)
-                return -errno;
-
-        if (d->wd < 0) {
-                r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d);
-                if (r < 0) {
-                        (void) inotify_rm_watch(d->inotify_data->fd, wd);
-                        return r;
-                }
-
-                d->wd = wd;
-
-        } else if (d->wd != wd) {
-
-                log_debug("Weird, the watch descriptor we already knew for this inode changed?");
-                (void) inotify_rm_watch(d->fd, wd);
-                return -EINVAL;
-        }
-
-        d->combined_mask = combined_mask;
-        return 1;
-}
-
-static int inotify_exit_callback(sd_event_source *s, const struct inotify_event *event, void *userdata) {
-        assert(s);
-
-        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
-}
-
-_public_ int sd_event_add_inotify(
-                sd_event *e,
-                sd_event_source **ret,
-                const char *path,
-                uint32_t mask,
-                sd_event_inotify_handler_t callback,
-                void *userdata) {
-
-        struct inotify_data *inotify_data = NULL;
-        struct inode_data *inode_data = NULL;
-        _cleanup_close_ int fd = -1;
-        _cleanup_(source_freep) sd_event_source *s = NULL;
-        struct stat st;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(path, -EINVAL);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!callback)
-                callback = inotify_exit_callback;
-
-        /* Refuse IN_MASK_ADD since we coalesce watches on the same inode, and hence really don't want to merge
-         * masks. Or in other words, this whole code exists only to manage IN_MASK_ADD type operations for you, hence
-         * the user can't use them for us. */
-        if (mask & IN_MASK_ADD)
-                return -EINVAL;
-
-        fd = open(path, O_PATH|O_CLOEXEC|
-                  (mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
-                  (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
-        if (fd < 0)
-                return -errno;
-
-        if (fstat(fd, &st) < 0)
-                return -errno;
-
-        s = source_new(e, !ret, SOURCE_INOTIFY);
-        if (!s)
-                return -ENOMEM;
-
-        s->enabled = mask & IN_ONESHOT ? SD_EVENT_ONESHOT : SD_EVENT_ON;
-        s->inotify.mask = mask;
-        s->inotify.callback = callback;
-        s->userdata = userdata;
-
-        /* Allocate an inotify object for this priority, and an inode object within it */
-        r = event_make_inotify_data(e, SD_EVENT_PRIORITY_NORMAL, &inotify_data);
-        if (r < 0)
-                return r;
-
-        r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data);
-        if (r < 0) {
-                event_free_inotify_data(e, inotify_data);
-                return r;
-        }
-
-        /* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of
-         * the event source, until then, for which we need the original inode. */
-        if (inode_data->fd < 0) {
-                inode_data->fd = TAKE_FD(fd);
-                LIST_PREPEND(to_close, e->inode_data_to_close, inode_data);
-        }
-
-        /* Link our event source to the inode data object */
-        LIST_PREPEND(inotify.by_inode_data, inode_data->event_sources, s);
-        s->inotify.inode_data = inode_data;
-
-        /* Actually realize the watch now */
-        r = inode_data_realize_watch(e, inode_data);
-        if (r < 0)
-                return r;
-
-        (void) sd_event_source_set_description(s, path);
-
-        if (ret)
-                *ret = s;
-        TAKE_PTR(s);
-
-        return 0;
-}
-
-static sd_event_source* event_source_free(sd_event_source *s) {
-        if (!s)
-                return NULL;
-
-        /* Here's a special hack: when we are called from a
-         * dispatch handler we won't free the event source
-         * immediately, but we will detach the fd from the
-         * epoll. This way it is safe for the caller to unref
-         * the event source and immediately close the fd, but
-         * we still retain a valid event source object after
-         * the callback. */
-
-        if (s->dispatching) {
-                if (s->type == SOURCE_IO)
-                        source_io_unregister(s);
-
-                source_disconnect(s);
-        } else
-                source_free(s);
-
-        return NULL;
-}
-
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event_source, sd_event_source, event_source_free);
-
-_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) {
-        assert_return(s, -EINVAL);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        return free_and_strdup(&s->description, description);
-}
-
-_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) {
-        assert_return(s, -EINVAL);
-        assert_return(description, -EINVAL);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        if (!s->description)
-                return -ENXIO;
-
-        *description = s->description;
-        return 0;
-}
-
-_public_ sd_event *sd_event_source_get_event(sd_event_source *s) {
-        assert_return(s, NULL);
-
-        return s->event;
-}
-
-_public_ int sd_event_source_get_pending(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type != SOURCE_EXIT, -EDOM);
-        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        return s->pending;
-}
-
-_public_ int sd_event_source_get_io_fd(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        return s->io.fd;
-}
-
-_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(fd >= 0, -EBADF);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        if (s->io.fd == fd)
-                return 0;
-
-        if (event_source_is_offline(s)) {
-                s->io.fd = fd;
-                s->io.registered = false;
-        } else {
-                int saved_fd;
-
-                saved_fd = s->io.fd;
-                assert(s->io.registered);
-
-                s->io.fd = fd;
-                s->io.registered = false;
-
-                r = source_io_register(s, s->enabled, s->io.events);
-                if (r < 0) {
-                        s->io.fd = saved_fd;
-                        s->io.registered = true;
-                        return r;
-                }
-
-                (void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
-        }
-
-        return 0;
-}
-
-_public_ int sd_event_source_get_io_fd_own(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-
-        return s->io.owned;
-}
-
-_public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-
-        s->io.owned = own;
-        return 0;
-}
-
-_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
-        assert_return(s, -EINVAL);
-        assert_return(events, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *events = s->io.events;
-        return 0;
-}
-
-_public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) {
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-        assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
-        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        /* edge-triggered updates are never skipped, so we can reset edges */
-        if (s->io.events == events && !(events & EPOLLET))
-                return 0;
-
-        r = source_set_pending(s, false);
-        if (r < 0)
-                return r;
-
-        if (event_source_is_online(s)) {
-                r = source_io_register(s, s->enabled, events);
-                if (r < 0)
-                        return r;
-        }
-
-        s->io.events = events;
-
-        return 0;
-}
-
-_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) {
-        assert_return(s, -EINVAL);
-        assert_return(revents, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-        assert_return(s->pending, -ENODATA);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *revents = s->io.revents;
-        return 0;
-}
-
-_public_ int sd_event_source_get_signal(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_SIGNAL, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        return s->signal.sig;
-}
-
-_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) {
-        assert_return(s, -EINVAL);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *priority = s->priority;
-        return 0;
-}
-
-_public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) {
-        bool rm_inotify = false, rm_inode = false;
-        struct inotify_data *new_inotify_data = NULL;
-        struct inode_data *new_inode_data = NULL;
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        if (s->priority == priority)
-                return 0;
-
-        if (s->type == SOURCE_INOTIFY) {
-                struct inode_data *old_inode_data;
-
-                assert(s->inotify.inode_data);
-                old_inode_data = s->inotify.inode_data;
-
-                /* We need the original fd to change the priority. If we don't have it we can't change the priority,
-                 * anymore. Note that we close any fds when entering the next event loop iteration, i.e. for inotify
-                 * events we allow priority changes only until the first following iteration. */
-                if (old_inode_data->fd < 0)
-                        return -EOPNOTSUPP;
-
-                r = event_make_inotify_data(s->event, priority, &new_inotify_data);
-                if (r < 0)
-                        return r;
-                rm_inotify = r > 0;
-
-                r = event_make_inode_data(s->event, new_inotify_data, old_inode_data->dev, old_inode_data->ino, &new_inode_data);
-                if (r < 0)
-                        goto fail;
-                rm_inode = r > 0;
-
-                if (new_inode_data->fd < 0) {
-                        /* Duplicate the fd for the new inode object if we don't have any yet */
-                        new_inode_data->fd = fcntl(old_inode_data->fd, F_DUPFD_CLOEXEC, 3);
-                        if (new_inode_data->fd < 0) {
-                                r = -errno;
-                                goto fail;
-                        }
-
-                        LIST_PREPEND(to_close, s->event->inode_data_to_close, new_inode_data);
-                }
-
-                /* Move the event source to the new inode data structure */
-                LIST_REMOVE(inotify.by_inode_data, old_inode_data->event_sources, s);
-                LIST_PREPEND(inotify.by_inode_data, new_inode_data->event_sources, s);
-                s->inotify.inode_data = new_inode_data;
-
-                /* Now create the new watch */
-                r = inode_data_realize_watch(s->event, new_inode_data);
-                if (r < 0) {
-                        /* Move it back */
-                        LIST_REMOVE(inotify.by_inode_data, new_inode_data->event_sources, s);
-                        LIST_PREPEND(inotify.by_inode_data, old_inode_data->event_sources, s);
-                        s->inotify.inode_data = old_inode_data;
-                        goto fail;
-                }
-
-                s->priority = priority;
-
-                event_gc_inode_data(s->event, old_inode_data);
-
-        } else if (s->type == SOURCE_SIGNAL && event_source_is_online(s)) {
-                struct signal_data *old, *d;
-
-                /* Move us from the signalfd belonging to the old
-                 * priority to the signalfd of the new priority */
-
-                assert_se(old = hashmap_get(s->event->signal_data, &s->priority));
-
-                s->priority = priority;
-
-                r = event_make_signal_data(s->event, s->signal.sig, &d);
-                if (r < 0) {
-                        s->priority = old->priority;
-                        return r;
-                }
-
-                event_unmask_signal_data(s->event, old, s->signal.sig);
-        } else
-                s->priority = priority;
-
-        event_source_pp_prioq_reshuffle(s);
-
-        if (s->type == SOURCE_EXIT)
-                prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
-
-        return 0;
-
-fail:
-        if (rm_inode)
-                event_free_inode_data(s->event, new_inode_data);
-
-        if (rm_inotify)
-                event_free_inotify_data(s->event, new_inotify_data);
-
-        return r;
-}
-
-_public_ int sd_event_source_get_enabled(sd_event_source *s, int *ret) {
-        assert_return(s, -EINVAL);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        if (ret)
-                *ret = s->enabled;
-
-        return s->enabled != SD_EVENT_OFF;
-}
-
-static int event_source_offline(
-                sd_event_source *s,
-                int enabled,
-                bool ratelimited) {
-
-        bool was_offline;
-        int r;
-
-        assert(s);
-        assert(enabled == SD_EVENT_OFF || ratelimited);
-
-        /* Unset the pending flag when this event source is disabled */
-        if (s->enabled != SD_EVENT_OFF &&
-            enabled == SD_EVENT_OFF &&
-            !IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
-                r = source_set_pending(s, false);
-                if (r < 0)
-                        return r;
-        }
-
-        was_offline = event_source_is_offline(s);
-        s->enabled = enabled;
-        s->ratelimited = ratelimited;
-
-        switch (s->type) {
-
-        case SOURCE_IO:
-                source_io_unregister(s);
-                break;
-
-        case SOURCE_TIME_REALTIME:
-        case SOURCE_TIME_BOOTTIME:
-        case SOURCE_TIME_MONOTONIC:
-        case SOURCE_TIME_REALTIME_ALARM:
-        case SOURCE_TIME_BOOTTIME_ALARM:
-                event_source_time_prioq_reshuffle(s);
-                break;
-
-        case SOURCE_SIGNAL:
-                event_gc_signal_data(s->event, &s->priority, s->signal.sig);
-                break;
-
-        case SOURCE_CHILD:
-                if (!was_offline) {
-                        assert(s->event->n_online_child_sources > 0);
-                        s->event->n_online_child_sources--;
-                }
-
-                if (EVENT_SOURCE_WATCH_PIDFD(s))
-                        source_child_pidfd_unregister(s);
-                else
-                        event_gc_signal_data(s->event, &s->priority, SIGCHLD);
-                break;
-
-        case SOURCE_EXIT:
-                prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
-                break;
-
-        case SOURCE_DEFER:
-        case SOURCE_POST:
-        case SOURCE_INOTIFY:
-                break;
-
-        default:
-                assert_not_reached("Wut? I shouldn't exist.");
-        }
-
-        return 1;
-}
-
-static int event_source_online(
-                sd_event_source *s,
-                int enabled,
-                bool ratelimited) {
-
-        bool was_online;
-        int r;
-
-        assert(s);
-        assert(enabled != SD_EVENT_OFF || !ratelimited);
-
-        /* Unset the pending flag when this event source is enabled */
-        if (s->enabled == SD_EVENT_OFF &&
-            enabled != SD_EVENT_OFF &&
-            !IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
-                r = source_set_pending(s, false);
-                if (r < 0)
-                        return r;
-        }
-
-        /* Are we really ready for onlining? */
-        if (enabled == SD_EVENT_OFF || ratelimited) {
-                /* Nope, we are not ready for onlining, then just update the precise state and exit */
-                s->enabled = enabled;
-                s->ratelimited = ratelimited;
-                return 0;
-        }
-
-        was_online = event_source_is_online(s);
-
-        switch (s->type) {
-        case SOURCE_IO:
-                r = source_io_register(s, enabled, s->io.events);
-                if (r < 0)
-                        return r;
-                break;
-
-        case SOURCE_SIGNAL:
-                r = event_make_signal_data(s->event, s->signal.sig, NULL);
-                if (r < 0) {
-                        event_gc_signal_data(s->event, &s->priority, s->signal.sig);
-                        return r;
-                }
-
-                break;
-
-        case SOURCE_CHILD:
-                if (EVENT_SOURCE_WATCH_PIDFD(s)) {
-                        /* yes, we have pidfd */
-
-                        r = source_child_pidfd_register(s, enabled);
-                        if (r < 0)
-                                return r;
-                } else {
-                        /* no pidfd, or something other to watch for than WEXITED */
-
-                        r = event_make_signal_data(s->event, SIGCHLD, NULL);
-                        if (r < 0) {
-                                event_gc_signal_data(s->event, &s->priority, SIGCHLD);
-                                return r;
-                        }
-                }
-
-                if (!was_online)
-                        s->event->n_online_child_sources++;
-                break;
-
-        case SOURCE_TIME_REALTIME:
-        case SOURCE_TIME_BOOTTIME:
-        case SOURCE_TIME_MONOTONIC:
-        case SOURCE_TIME_REALTIME_ALARM:
-        case SOURCE_TIME_BOOTTIME_ALARM:
-        case SOURCE_EXIT:
-        case SOURCE_DEFER:
-        case SOURCE_POST:
-        case SOURCE_INOTIFY:
-                break;
-
-        default:
-                assert_not_reached("Wut? I shouldn't exist.");
-        }
-
-        s->enabled = enabled;
-        s->ratelimited = ratelimited;
-
-        /* Non-failing operations below */
-        switch (s->type) {
-        case SOURCE_TIME_REALTIME:
-        case SOURCE_TIME_BOOTTIME:
-        case SOURCE_TIME_MONOTONIC:
-        case SOURCE_TIME_REALTIME_ALARM:
-        case SOURCE_TIME_BOOTTIME_ALARM:
-                event_source_time_prioq_reshuffle(s);
-                break;
-
-        case SOURCE_EXIT:
-                prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
-                break;
-
-        default:
-                break;
-        }
-
-        return 1;
-}
-
-_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(IN_SET(m, SD_EVENT_OFF, SD_EVENT_ON, SD_EVENT_ONESHOT), -EINVAL);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        /* If we are dead anyway, we are fine with turning off sources, but everything else needs to fail. */
-        if (s->event->state == SD_EVENT_FINISHED)
-                return m == SD_EVENT_OFF ? 0 : -ESTALE;
-
-        if (s->enabled == m) /* No change? */
-                return 0;
-
-        if (m == SD_EVENT_OFF)
-                r = event_source_offline(s, m, s->ratelimited);
-        else {
-                if (s->enabled != SD_EVENT_OFF) {
-                        /* Switching from "on" to "oneshot" or back? If that's the case, we can take a shortcut, the
-                         * event source is already enabled after all. */
-                        s->enabled = m;
-                        return 0;
-                }
-
-                r = event_source_online(s, m, s->ratelimited);
-        }
-        if (r < 0)
-                return r;
-
-        event_source_pp_prioq_reshuffle(s);
-        return 0;
-}
-
-_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) {
-        assert_return(s, -EINVAL);
-        assert_return(usec, -EINVAL);
-        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *usec = s->time.next;
-        return 0;
-}
-
-_public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) {
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
-        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        r = source_set_pending(s, false);
-        if (r < 0)
-                return r;
-
-        s->time.next = usec;
-
-        event_source_time_prioq_reshuffle(s);
-        return 0;
-}
-
-_public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec) {
-        usec_t t;
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
-
-        r = sd_event_now(s->event, event_source_type_to_clock(s->type), &t);
-        if (r < 0)
-                return r;
-
-        if (usec >= USEC_INFINITY - t)
-                return -EOVERFLOW;
-
-        return sd_event_source_set_time(s, t + usec);
-}
-
-_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) {
-        assert_return(s, -EINVAL);
-        assert_return(usec, -EINVAL);
-        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *usec = s->time.accuracy;
-        return 0;
-}
-
-_public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) {
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(usec != (uint64_t) -1, -EINVAL);
-        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
-        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        r = source_set_pending(s, false);
-        if (r < 0)
-                return r;
-
-        if (usec == 0)
-                usec = DEFAULT_ACCURACY_USEC;
-
-        s->time.accuracy = usec;
-
-        event_source_time_prioq_reshuffle(s);
-        return 0;
-}
-
-_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) {
-        assert_return(s, -EINVAL);
-        assert_return(clock, -EINVAL);
-        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *clock = event_source_type_to_clock(s->type);
-        return 0;
-}
-
-_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
-        assert_return(s, -EINVAL);
-        assert_return(pid, -EINVAL);
-        assert_return(s->type == SOURCE_CHILD, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *pid = s->child.pid;
-        return 0;
-}
-
-_public_ int sd_event_source_get_child_pidfd(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_CHILD, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        if (s->child.pidfd < 0)
-                return -EOPNOTSUPP;
-
-        return s->child.pidfd;
-}
-
-_public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo_t *si, unsigned flags) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_CHILD, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-        assert_return(SIGNAL_VALID(sig), -EINVAL);
-
-        /* If we already have seen indication the process exited refuse sending a signal early. This way we
-         * can be sure we don't accidentally kill the wrong process on PID reuse when pidfds are not
-         * available. */
-        if (s->child.exited)
-                return -ESRCH;
-
-        if (s->child.pidfd >= 0) {
-                siginfo_t copy;
-
-                /* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the
-                 * structure here */
-                if (si)
-                        copy = *si;
-
-                if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0) {
-                        /* Let's propagate the error only if the system call is not implemented or prohibited */
-                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
-                                return -errno;
-                } else
-                        return 0;
-        }
-
-        /* Flags are only supported for pidfd_send_signal(), not for rt_sigqueueinfo(), hence let's refuse
-         * this here. */
-        if (flags != 0)
-                return -EOPNOTSUPP;
-
-        if (si) {
-                /* We use rt_sigqueueinfo() only if siginfo_t is specified. */
-                siginfo_t copy = *si;
-
-                if (rt_sigqueueinfo(s->child.pid, sig, &copy) < 0)
-                        return -errno;
-        } else if (kill(s->child.pid, sig) < 0)
-                return -errno;
-
-        return 0;
-}
-
-_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_CHILD, -EDOM);
-
-        if (s->child.pidfd < 0)
-                return -EOPNOTSUPP;
-
-        return s->child.pidfd_owned;
-}
-
-_public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_CHILD, -EDOM);
-
-        if (s->child.pidfd < 0)
-                return -EOPNOTSUPP;
-
-        s->child.pidfd_owned = own;
-        return 0;
-}
-
-_public_ int sd_event_source_get_child_process_own(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_CHILD, -EDOM);
-
-        return s->child.process_owned;
-}
-
-_public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_CHILD, -EDOM);
-
-        s->child.process_owned = own;
-        return 0;
-}
-
-_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) {
-        assert_return(s, -EINVAL);
-        assert_return(mask, -EINVAL);
-        assert_return(s->type == SOURCE_INOTIFY, -EDOM);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        *mask = s->inotify.mask;
-        return 0;
-}
-
-_public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) {
-        int r;
-
-        assert_return(s, -EINVAL);
-        assert_return(s->type != SOURCE_EXIT, -EDOM);
-        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(s->event), -ECHILD);
-
-        if (s->prepare == callback)
-                return 0;
-
-        if (callback && s->prepare) {
-                s->prepare = callback;
-                return 0;
-        }
-
-        r = prioq_ensure_allocated(&s->event->prepare, prepare_prioq_compare);
-        if (r < 0)
-                return r;
-
-        s->prepare = callback;
-
-        if (callback) {
-                r = prioq_put(s->event->prepare, s, &s->prepare_index);
-                if (r < 0)
-                        return r;
-        } else
-                prioq_remove(s->event->prepare, s, &s->prepare_index);
-
-        return 0;
-}
-
-_public_ void* sd_event_source_get_userdata(sd_event_source *s) {
-        assert_return(s, NULL);
-
-        return s->userdata;
-}
-
-_public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) {
-        void *ret;
-
-        assert_return(s, NULL);
-
-        ret = s->userdata;
-        s->userdata = userdata;
-
-        return ret;
-}
-
-static int event_source_enter_ratelimited(sd_event_source *s) {
-        int r;
-
-        assert(s);
-
-        /* When an event source becomes ratelimited, we place it in the CLOCK_MONOTONIC priority queue, with
-         * the end of the rate limit time window, much as if it was a timer event source. */
-
-        if (s->ratelimited)
-                return 0; /* Already ratelimited, this is a NOP hence */
-
-        /* Make sure we can install a CLOCK_MONOTONIC event further down. */
-        r = setup_clock_data(s->event, &s->event->monotonic, CLOCK_MONOTONIC);
-        if (r < 0)
-                return r;
-
-        /* Timer event sources are already using the earliest/latest queues for the timer scheduling. Let's
-         * first remove them from the prioq appropriate for their own clock, so that we can use the prioq
-         * fields of the event source then for adding it to the CLOCK_MONOTONIC prioq instead. */
-        if (EVENT_SOURCE_IS_TIME(s->type))
-                event_source_time_prioq_remove(s, event_get_clock_data(s->event, s->type));
-
-        /* Now, let's add the event source to the monotonic clock instead */
-        r = event_source_time_prioq_put(s, &s->event->monotonic);
-        if (r < 0)
-                goto fail;
-
-        /* And let's take the event source officially offline */
-        r = event_source_offline(s, s->enabled, /* ratelimited= */ true);
-        if (r < 0) {
-                event_source_time_prioq_remove(s, &s->event->monotonic);
-                goto fail;
-        }
-
-        event_source_pp_prioq_reshuffle(s);
-
-        log_debug("Event source %p (%s) entered rate limit state.", s, strna(s->description));
-        return 0;
-
-fail:
-        /* Reinstall time event sources in the priority queue as before. This shouldn't fail, since the queue
-         * space for it should already be allocated. */
-        if (EVENT_SOURCE_IS_TIME(s->type))
-                assert_se(event_source_time_prioq_put(s, event_get_clock_data(s->event, s->type)) >= 0);
-
-        return r;
-}
-
-static int event_source_leave_ratelimit(sd_event_source *s) {
-        int r;
-
-        assert(s);
-
-        if (!s->ratelimited)
-                return 0;
-
-        /* Let's take the event source out of the monotonic prioq first. */
-        event_source_time_prioq_remove(s, &s->event->monotonic);
-
-        /* Let's then add the event source to its native clock prioq again — if this is a timer event source */
-        if (EVENT_SOURCE_IS_TIME(s->type)) {
-                r = event_source_time_prioq_put(s, event_get_clock_data(s->event, s->type));
-                if (r < 0)
-                        goto fail;
-        }
-
-        /* Let's try to take it online again.  */
-        r = event_source_online(s, s->enabled, /* ratelimited= */ false);
-        if (r < 0) {
-                /* Do something roughly sensible when this failed: undo the two prioq ops above */
-                if (EVENT_SOURCE_IS_TIME(s->type))
-                        event_source_time_prioq_remove(s, event_get_clock_data(s->event, s->type));
-
-                goto fail;
-        }
-
-        event_source_pp_prioq_reshuffle(s);
-        ratelimit_reset(&s->rate_limit);
-
-        log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description));
-        return 0;
-
-fail:
-        /* Do something somewhat reasonable when we cannot move an event sources out of ratelimited mode:
-         * simply put it back in it, maybe we can then process it more successfully next iteration. */
-        assert_se(event_source_time_prioq_put(s, &s->event->monotonic) >= 0);
-
-        return r;
-}
-
-static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) {
-        usec_t c;
-        assert(e);
-        assert(a <= b);
-
-        if (a <= 0)
-                return 0;
-        if (a >= USEC_INFINITY)
-                return USEC_INFINITY;
-
-        if (b <= a + 1)
-                return a;
-
-        initialize_perturb(e);
-
-        /*
-          Find a good time to wake up again between times a and b. We
-          have two goals here:
-
-          a) We want to wake up as seldom as possible, hence prefer
-             later times over earlier times.
-
-          b) But if we have to wake up, then let's make sure to
-             dispatch as much as possible on the entire system.
-
-          We implement this by waking up everywhere at the same time
-          within any given minute if we can, synchronised via the
-          perturbation value determined from the boot ID. If we can't,
-          then we try to find the same spot in every 10s, then 1s and
-          then 250ms step. Otherwise, we pick the last possible time
-          to wake up.
-        */
-
-        c = (b / USEC_PER_MINUTE) * USEC_PER_MINUTE + e->perturb;
-        if (c >= b) {
-                if (_unlikely_(c < USEC_PER_MINUTE))
-                        return b;
-
-                c -= USEC_PER_MINUTE;
-        }
-
-        if (c >= a)
-                return c;
-
-        c = (b / (USEC_PER_SEC*10)) * (USEC_PER_SEC*10) + (e->perturb % (USEC_PER_SEC*10));
-        if (c >= b) {
-                if (_unlikely_(c < USEC_PER_SEC*10))
-                        return b;
-
-                c -= USEC_PER_SEC*10;
-        }
-
-        if (c >= a)
-                return c;
-
-        c = (b / USEC_PER_SEC) * USEC_PER_SEC + (e->perturb % USEC_PER_SEC);
-        if (c >= b) {
-                if (_unlikely_(c < USEC_PER_SEC))
-                        return b;
-
-                c -= USEC_PER_SEC;
-        }
-
-        if (c >= a)
-                return c;
-
-        c = (b / (USEC_PER_MSEC*250)) * (USEC_PER_MSEC*250) + (e->perturb % (USEC_PER_MSEC*250));
-        if (c >= b) {
-                if (_unlikely_(c < USEC_PER_MSEC*250))
-                        return b;
-
-                c -= USEC_PER_MSEC*250;
-        }
-
-        if (c >= a)
-                return c;
-
-        return b;
-}
-
-static int event_arm_timer(
-                sd_event *e,
-                struct clock_data *d) {
-
-        struct itimerspec its = {};
-        sd_event_source *a, *b;
-        usec_t t;
-
-        assert(e);
-        assert(d);
-
-        if (!d->needs_rearm)
-                return 0;
-        else
-                d->needs_rearm = false;
-
-        a = prioq_peek(d->earliest);
-        if (!a || a->enabled == SD_EVENT_OFF || time_event_source_next(a) == USEC_INFINITY) {
-
-                if (d->fd < 0)
-                        return 0;
-
-                if (d->next == USEC_INFINITY)
-                        return 0;
-
-                /* disarm */
-                if (timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL) < 0)
-                        return -errno;
-
-                d->next = USEC_INFINITY;
-                return 0;
-        }
-
-        b = prioq_peek(d->latest);
-        assert_se(b && b->enabled != SD_EVENT_OFF);
-
-        t = sleep_between(e, time_event_source_next(a), time_event_source_latest(b));
-        if (d->next == t)
-                return 0;
-
-        assert_se(d->fd >= 0);
-
-        if (t == 0) {
-                /* We don' want to disarm here, just mean some time looooong ago. */
-                its.it_value.tv_sec = 0;
-                its.it_value.tv_nsec = 1;
-        } else
-                timespec_store(&its.it_value, t);
-
-        if (timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL) < 0)
-                return -errno;
-
-        d->next = t;
-        return 0;
-}
-
-static int process_io(sd_event *e, sd_event_source *s, uint32_t revents) {
-        assert(e);
-        assert(s);
-        assert(s->type == SOURCE_IO);
-
-        /* If the event source was already pending, we just OR in the
-         * new revents, otherwise we reset the value. The ORing is
-         * necessary to handle EPOLLONESHOT events properly where
-         * readability might happen independently of writability, and
-         * we need to keep track of both */
-
-        if (s->pending)
-                s->io.revents |= revents;
-        else
-                s->io.revents = revents;
-
-        return source_set_pending(s, true);
-}
-
-static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) {
-        uint64_t x;
-        ssize_t ss;
-
-        assert(e);
-        assert(fd >= 0);
-
-        assert_return(events == EPOLLIN, -EIO);
-
-        ss = read(fd, &x, sizeof(x));
-        if (ss < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
-                        return 0;
-
-                return -errno;
-        }
-
-        if (_unlikely_(ss != sizeof(x)))
-                return -EIO;
-
-        if (next)
-                *next = USEC_INFINITY;
-
-        return 0;
-}
-
-static int process_timer(
-                sd_event *e,
-                usec_t n,
-                struct clock_data *d) {
-
-        sd_event_source *s;
-        int r;
-
-        assert(e);
-        assert(d);
-
-        for (;;) {
-                s = prioq_peek(d->earliest);
-                if (!s || time_event_source_next(s) > n)
-                        break;
-
-                if (s->ratelimited) {
-                        /* This is an event sources whose ratelimit window has ended. Let's turn it on
-                         * again. */
-                        assert(s->ratelimited);
-
-                        r = event_source_leave_ratelimit(s);
-                        if (r < 0)
-                                return r;
-
-                        continue;
-                }
-
-                if (s->enabled == SD_EVENT_OFF || s->pending)
-                        break;
-
-                r = source_set_pending(s, true);
-                if (r < 0)
-                        return r;
-
-                event_source_time_prioq_reshuffle(s);
-        }
-
-        return 0;
-}
-
-static int process_child(sd_event *e) {
-        sd_event_source *s;
-        int r;
-
-        assert(e);
-
-        e->need_process_child = false;
-
-        /*
-           So, this is ugly. We iteratively invoke waitid() with P_PID
-           + WNOHANG for each PID we wait for, instead of using
-           P_ALL. This is because we only want to get child
-           information of very specific child processes, and not all
-           of them. We might not have processed the SIGCHLD even of a
-           previous invocation and we don't want to maintain a
-           unbounded *per-child* event queue, hence we really don't
-           want anything flushed out of the kernel's queue that we
-           don't care about. Since this is O(n) this means that if you
-           have a lot of processes you probably want to handle SIGCHLD
-           yourself.
-
-           We do not reap the children here (by using WNOWAIT), this
-           is only done after the event source is dispatched so that
-           the callback still sees the process as a zombie.
-        */
-
-        HASHMAP_FOREACH(s, e->child_sources) {
-                assert(s->type == SOURCE_CHILD);
-
-                if (s->pending)
-                        continue;
-
-                if (event_source_is_offline(s))
-                        continue;
-
-                if (s->child.exited)
-                        continue;
-
-                if (EVENT_SOURCE_WATCH_PIDFD(s)) /* There's a usable pidfd known for this event source? then don't waitid() for it here */
-                        continue;
-
-                zero(s->child.siginfo);
-                if (waitid(P_PID, s->child.pid, &s->child.siginfo,
-                           WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
-                        return -errno;
-
-                if (s->child.siginfo.si_pid != 0) {
-                        bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
-
-                        if (zombie)
-                                s->child.exited = true;
-
-                        if (!zombie && (s->child.options & WEXITED)) {
-                                /* If the child isn't dead then let's
-                                 * immediately remove the state change
-                                 * from the queue, since there's no
-                                 * benefit in leaving it queued */
-
-                                assert(s->child.options & (WSTOPPED|WCONTINUED));
-                                (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
-                        }
-
-                        r = source_set_pending(s, true);
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        return 0;
-}
-
-static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
-        assert(e);
-        assert(s);
-        assert(s->type == SOURCE_CHILD);
-
-        if (s->pending)
-                return 0;
-
-        if (event_source_is_offline(s))
-                return 0;
-
-        if (!EVENT_SOURCE_WATCH_PIDFD(s))
-                return 0;
-
-        zero(s->child.siginfo);
-        if (waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
-                return -errno;
-
-        if (s->child.siginfo.si_pid == 0)
-                return 0;
-
-        if (IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED))
-                s->child.exited = true;
-
-        return source_set_pending(s, true);
-}
-
-static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
-        bool read_one = false;
-        int r;
-
-        assert(e);
-        assert(d);
-        assert_return(events == EPOLLIN, -EIO);
-
-        /* If there's a signal queued on this priority and SIGCHLD is
-           on this priority too, then make sure to recheck the
-           children we watch. This is because we only ever dequeue
-           the first signal per priority, and if we dequeue one, and
-           SIGCHLD might be enqueued later we wouldn't know, but we
-           might have higher priority children we care about hence we
-           need to check that explicitly. */
-
-        if (sigismember(&d->sigset, SIGCHLD))
-                e->need_process_child = true;
-
-        /* If there's already an event source pending for this
-         * priority we don't read another */
-        if (d->current)
-                return 0;
-
-        for (;;) {
-                struct signalfd_siginfo si;
-                ssize_t n;
-                sd_event_source *s = NULL;
-
-                n = read(d->fd, &si, sizeof(si));
-                if (n < 0) {
-                        if (IN_SET(errno, EAGAIN, EINTR))
-                                return read_one;
-
-                        return -errno;
-                }
-
-                if (_unlikely_(n != sizeof(si)))
-                        return -EIO;
-
-                assert(SIGNAL_VALID(si.ssi_signo));
-
-                read_one = true;
-
-                if (e->signal_sources)
-                        s = e->signal_sources[si.ssi_signo];
-                if (!s)
-                        continue;
-                if (s->pending)
-                        continue;
-
-                s->signal.siginfo = si;
-                d->current = s;
-
-                r = source_set_pending(s, true);
-                if (r < 0)
-                        return r;
-
-                return 1;
-        }
-}
-
-static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents) {
-        ssize_t n;
-
-        assert(e);
-        assert(d);
-
-        assert_return(revents == EPOLLIN, -EIO);
-
-        /* If there's already an event source pending for this priority, don't read another */
-        if (d->n_pending > 0)
-                return 0;
-
-        /* Is the read buffer non-empty? If so, let's not read more */
-        if (d->buffer_filled > 0)
-                return 0;
-
-        n = read(d->fd, &d->buffer, sizeof(d->buffer));
-        if (n < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
-                        return 0;
-
-                return -errno;
-        }
-
-        assert(n > 0);
-        d->buffer_filled = (size_t) n;
-        LIST_PREPEND(buffered, e->inotify_data_buffered, d);
-
-        return 1;
-}
-
-static void event_inotify_data_drop(sd_event *e, struct inotify_data *d, size_t sz) {
-        assert(e);
-        assert(d);
-        assert(sz <= d->buffer_filled);
-
-        if (sz == 0)
-                return;
-
-        /* Move the rest to the buffer to the front, in order to get things properly aligned again */
-        memmove(d->buffer.raw, d->buffer.raw + sz, d->buffer_filled - sz);
-        d->buffer_filled -= sz;
-
-        if (d->buffer_filled == 0)
-                LIST_REMOVE(buffered, e->inotify_data_buffered, d);
-}
-
-static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
-        int r;
-
-        assert(e);
-        assert(d);
-
-        /* If there's already an event source pending for this priority, don't read another */
-        if (d->n_pending > 0)
-                return 0;
-
-        while (d->buffer_filled > 0) {
-                size_t sz;
-
-                /* Let's validate that the event structures are complete */
-                if (d->buffer_filled < offsetof(struct inotify_event, name))
-                        return -EIO;
-
-                sz = offsetof(struct inotify_event, name) + d->buffer.ev.len;
-                if (d->buffer_filled < sz)
-                        return -EIO;
-
-                if (d->buffer.ev.mask & IN_Q_OVERFLOW) {
-                        struct inode_data *inode_data;
-
-                        /* The queue overran, let's pass this event to all event sources connected to this inotify
-                         * object */
-
-                        HASHMAP_FOREACH(inode_data, d->inodes) {
-                                sd_event_source *s;
-
-                                LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) {
-
-                                        if (event_source_is_offline(s))
-                                                continue;
-
-                                        r = source_set_pending(s, true);
-                                        if (r < 0)
-                                                return r;
-                                }
-                        }
-                } else {
-                        struct inode_data *inode_data;
-                        sd_event_source *s;
-
-                        /* Find the inode object for this watch descriptor. If IN_IGNORED is set we also remove it from
-                         * our watch descriptor table. */
-                        if (d->buffer.ev.mask & IN_IGNORED) {
-
-                                inode_data = hashmap_remove(d->wd, INT_TO_PTR(d->buffer.ev.wd));
-                                if (!inode_data) {
-                                        event_inotify_data_drop(e, d, sz);
-                                        continue;
-                                }
-
-                                /* The watch descriptor was removed by the kernel, let's drop it here too */
-                                inode_data->wd = -1;
-                        } else {
-                                inode_data = hashmap_get(d->wd, INT_TO_PTR(d->buffer.ev.wd));
-                                if (!inode_data) {
-                                        event_inotify_data_drop(e, d, sz);
-                                        continue;
-                                }
-                        }
-
-                        /* Trigger all event sources that are interested in these events. Also trigger all event
-                         * sources if IN_IGNORED or IN_UNMOUNT is set. */
-                        LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) {
-
-                                if (event_source_is_offline(s))
-                                        continue;
-
-                                if ((d->buffer.ev.mask & (IN_IGNORED|IN_UNMOUNT)) == 0 &&
-                                    (s->inotify.mask & d->buffer.ev.mask & IN_ALL_EVENTS) == 0)
-                                        continue;
-
-                                r = source_set_pending(s, true);
-                                if (r < 0)
-                                        return r;
-                        }
-                }
-
-                /* Something pending now? If so, let's finish, otherwise let's read more. */
-                if (d->n_pending > 0)
-                        return 1;
-        }
-
-        return 0;
-}
-
-static int process_inotify(sd_event *e) {
-        struct inotify_data *d;
-        int r, done = 0;
-
-        assert(e);
-
-        LIST_FOREACH(buffered, d, e->inotify_data_buffered) {
-                r = event_inotify_data_process(e, d);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        done ++;
-        }
-
-        return done;
-}
-
-static int source_dispatch(sd_event_source *s) {
-        _cleanup_(sd_event_unrefp) sd_event *saved_event = NULL;
-        EventSourceType saved_type;
-        int r = 0;
-
-        assert(s);
-        assert(s->pending || s->type == SOURCE_EXIT);
-
-        /* Save the event source type, here, so that we still know it after the event callback which might
-         * invalidate the event. */
-        saved_type = s->type;
-
-        /* Similar, store a reference to the event loop object, so that we can still access it after the
-         * callback might have invalidated/disconnected the event source. */
-        saved_event = sd_event_ref(s->event);
-
-        /* Check if we hit the ratelimit for this event source, if so, let's disable it. */
-        assert(!s->ratelimited);
-        if (!ratelimit_below(&s->rate_limit)) {
-                r = event_source_enter_ratelimited(s);
-                if (r < 0)
-                        return r;
-
-                return 1;
-        }
-
-        if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
-                r = source_set_pending(s, false);
-                if (r < 0)
-                        return r;
-        }
-
-        if (s->type != SOURCE_POST) {
-                sd_event_source *z;
-
-                /* If we execute a non-post source, let's mark all
-                 * post sources as pending */
-
-                SET_FOREACH(z, s->event->post_sources) {
-                        if (event_source_is_offline(z))
-                                continue;
-
-                        r = source_set_pending(z, true);
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        if (s->enabled == SD_EVENT_ONESHOT) {
-                r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
-                if (r < 0)
-                        return r;
-        }
-
-        s->dispatching = true;
-
-        switch (s->type) {
-
-        case SOURCE_IO:
-                r = s->io.callback(s, s->io.fd, s->io.revents, s->userdata);
-                break;
-
-        case SOURCE_TIME_REALTIME:
-        case SOURCE_TIME_BOOTTIME:
-        case SOURCE_TIME_MONOTONIC:
-        case SOURCE_TIME_REALTIME_ALARM:
-        case SOURCE_TIME_BOOTTIME_ALARM:
-                r = s->time.callback(s, s->time.next, s->userdata);
-                break;
-
-        case SOURCE_SIGNAL:
-                r = s->signal.callback(s, &s->signal.siginfo, s->userdata);
-                break;
-
-        case SOURCE_CHILD: {
-                bool zombie;
-
-                zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
-
-                r = s->child.callback(s, &s->child.siginfo, s->userdata);
-
-                /* Now, reap the PID for good. */
-                if (zombie) {
-                        (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
-                        s->child.waited = true;
-                }
-
-                break;
-        }
-
-        case SOURCE_DEFER:
-                r = s->defer.callback(s, s->userdata);
-                break;
-
-        case SOURCE_POST:
-                r = s->post.callback(s, s->userdata);
-                break;
-
-        case SOURCE_EXIT:
-                r = s->exit.callback(s, s->userdata);
-                break;
-
-        case SOURCE_INOTIFY: {
-                struct sd_event *e = s->event;
-                struct inotify_data *d;
-                size_t sz;
-
-                assert(s->inotify.inode_data);
-                assert_se(d = s->inotify.inode_data->inotify_data);
-
-                assert(d->buffer_filled >= offsetof(struct inotify_event, name));
-                sz = offsetof(struct inotify_event, name) + d->buffer.ev.len;
-                assert(d->buffer_filled >= sz);
-
-                r = s->inotify.callback(s, &d->buffer.ev, s->userdata);
-
-                /* When no event is pending anymore on this inotify object, then let's drop the event from the
-                 * buffer. */
-                if (d->n_pending == 0)
-                        event_inotify_data_drop(e, d, sz);
-
-                break;
-        }
-
-        case SOURCE_WATCHDOG:
-        case _SOURCE_EVENT_SOURCE_TYPE_MAX:
-        case _SOURCE_EVENT_SOURCE_TYPE_INVALID:
-                assert_not_reached("Wut? I shouldn't exist.");
-        }
-
-        s->dispatching = false;
-
-        if (r < 0) {
-                log_debug_errno(r, "Event source %s (type %s) returned error, %s: %m",
-                                strna(s->description),
-                                event_source_type_to_string(saved_type),
-                                s->exit_on_failure ? "exiting" : "disabling");
-
-                if (s->exit_on_failure)
-                        (void) sd_event_exit(saved_event, r);
-        }
-
-        if (s->n_ref == 0)
-                source_free(s);
-        else if (r < 0)
-                sd_event_source_set_enabled(s, SD_EVENT_OFF);
-
-        return 1;
-}
-
-static int event_prepare(sd_event *e) {
-        int r;
-
-        assert(e);
-
-        for (;;) {
-                sd_event_source *s;
-
-                s = prioq_peek(e->prepare);
-                if (!s || s->prepare_iteration == e->iteration || event_source_is_offline(s))
-                        break;
-
-                s->prepare_iteration = e->iteration;
-                r = prioq_reshuffle(e->prepare, s, &s->prepare_index);
-                if (r < 0)
-                        return r;
-
-                assert(s->prepare);
-
-                s->dispatching = true;
-                r = s->prepare(s, s->userdata);
-                s->dispatching = false;
-
-                if (r < 0) {
-                        log_debug_errno(r, "Prepare callback of event source %s (type %s) returned error, %s: %m",
-                                        strna(s->description),
-                                        event_source_type_to_string(s->type),
-                                        s->exit_on_failure ? "exiting" : "disabling");
-
-                        if (s->exit_on_failure)
-                                (void) sd_event_exit(e, r);
-                }
-
-                if (s->n_ref == 0)
-                        source_free(s);
-                else if (r < 0)
-                        sd_event_source_set_enabled(s, SD_EVENT_OFF);
-        }
-
-        return 0;
-}
-
-static int dispatch_exit(sd_event *e) {
-        sd_event_source *p;
-        int r;
-
-        assert(e);
-
-        p = prioq_peek(e->exit);
-        if (!p || event_source_is_offline(p)) {
-                e->state = SD_EVENT_FINISHED;
-                return 0;
-        }
-
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
-        e->iteration++;
-        e->state = SD_EVENT_EXITING;
-        r = source_dispatch(p);
-        e->state = SD_EVENT_INITIAL;
-        return r;
-}
-
-static sd_event_source* event_next_pending(sd_event *e) {
-        sd_event_source *p;
-
-        assert(e);
-
-        p = prioq_peek(e->pending);
-        if (!p)
-                return NULL;
-
-        if (event_source_is_offline(p))
-                return NULL;
-
-        return p;
-}
-
-static int arm_watchdog(sd_event *e) {
-        struct itimerspec its = {};
-        usec_t t;
-
-        assert(e);
-        assert(e->watchdog_fd >= 0);
-
-        t = sleep_between(e,
-                          e->watchdog_last + (e->watchdog_period / 2),
-                          e->watchdog_last + (e->watchdog_period * 3 / 4));
-
-        timespec_store(&its.it_value, t);
-
-        /* Make sure we never set the watchdog to 0, which tells the
-         * kernel to disable it. */
-        if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
-                its.it_value.tv_nsec = 1;
-
-        if (timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL) < 0)
-                return -errno;
-
-        return 0;
-}
-
-static int process_watchdog(sd_event *e) {
-        assert(e);
-
-        if (!e->watchdog)
-                return 0;
-
-        /* Don't notify watchdog too often */
-        if (e->watchdog_last + e->watchdog_period / 4 > e->timestamp.monotonic)
-                return 0;
-
-        sd_notify(false, "WATCHDOG=1");
-        e->watchdog_last = e->timestamp.monotonic;
-
-        return arm_watchdog(e);
-}
-
-static void event_close_inode_data_fds(sd_event *e) {
-        struct inode_data *d;
-
-        assert(e);
-
-        /* Close the fds pointing to the inodes to watch now. We need to close them as they might otherwise pin
-         * filesystems. But we can't close them right-away as we need them as long as the user still wants to make
-         * adjustments to the even source, such as changing the priority (which requires us to remove and re-add a watch
-         * for the inode). Hence, let's close them when entering the first iteration after they were added, as a
-         * compromise. */
-
-        while ((d = e->inode_data_to_close)) {
-                assert(d->fd >= 0);
-                d->fd = safe_close(d->fd);
-
-                LIST_REMOVE(to_close, e->inode_data_to_close, d);
-        }
-}
-
-_public_ int sd_event_prepare(sd_event *e) {
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
-
-        /* Let's check that if we are a default event loop we are executed in the correct thread. We only do
-         * this check here once, since gettid() is typically not cached, and thus want to minimize
-         * syscalls */
-        assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO);
-
-        /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
-
-        if (e->exit_requested)
-                goto pending;
-
-        e->iteration++;
-
-        e->state = SD_EVENT_PREPARING;
-        r = event_prepare(e);
-        e->state = SD_EVENT_INITIAL;
-        if (r < 0)
-                return r;
-
-        r = event_arm_timer(e, &e->realtime);
-        if (r < 0)
-                return r;
-
-        r = event_arm_timer(e, &e->boottime);
-        if (r < 0)
-                return r;
-
-        r = event_arm_timer(e, &e->monotonic);
-        if (r < 0)
-                return r;
-
-        r = event_arm_timer(e, &e->realtime_alarm);
-        if (r < 0)
-                return r;
-
-        r = event_arm_timer(e, &e->boottime_alarm);
-        if (r < 0)
-                return r;
-
-        event_close_inode_data_fds(e);
-
-        if (event_next_pending(e) || e->need_process_child)
-                goto pending;
-
-        e->state = SD_EVENT_ARMED;
-
-        return 0;
-
-pending:
-        e->state = SD_EVENT_ARMED;
-        r = sd_event_wait(e, 0);
-        if (r == 0)
-                e->state = SD_EVENT_ARMED;
-
-        return r;
-}
-
-_public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
-        size_t event_queue_max;
-        int r, m, i;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
-
-        if (e->exit_requested) {
-                e->state = SD_EVENT_PENDING;
-                return 1;
-        }
-
-        event_queue_max = MAX(e->n_sources, 1u);
-        if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, event_queue_max))
-                return -ENOMEM;
-
-        /* If we still have inotify data buffered, then query the other fds, but don't wait on it */
-        if (e->inotify_data_buffered)
-                timeout = 0;
-
-        m = epoll_wait(e->epoll_fd, e->event_queue, event_queue_max,
-                       timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC));
-        if (m < 0) {
-                if (errno == EINTR) {
-                        e->state = SD_EVENT_PENDING;
-                        return 1;
-                }
-
-                r = -errno;
-                goto finish;
-        }
-
-        triple_timestamp_get(&e->timestamp);
-
-        for (i = 0; i < m; i++) {
-
-                if (e->event_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
-                        r = flush_timer(e, e->watchdog_fd, e->event_queue[i].events, NULL);
-                else {
-                        WakeupType *t = e->event_queue[i].data.ptr;
-
-                        switch (*t) {
-
-                        case WAKEUP_EVENT_SOURCE: {
-                                sd_event_source *s = e->event_queue[i].data.ptr;
-
-                                assert(s);
-
-                                switch (s->type) {
-
-                                case SOURCE_IO:
-                                        r = process_io(e, s, e->event_queue[i].events);
-                                        break;
-
-                                case SOURCE_CHILD:
-                                        r = process_pidfd(e, s, e->event_queue[i].events);
-                                        break;
-
-                                default:
-                                        assert_not_reached("Unexpected event source type");
-                                }
-
-                                break;
-                        }
-
-                        case WAKEUP_CLOCK_DATA: {
-                                struct clock_data *d = e->event_queue[i].data.ptr;
-
-                                assert(d);
-
-                                r = flush_timer(e, d->fd, e->event_queue[i].events, &d->next);
-                                break;
-                        }
-
-                        case WAKEUP_SIGNAL_DATA:
-                                r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events);
-                                break;
-
-                        case WAKEUP_INOTIFY_DATA:
-                                r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events);
-                                break;
-
-                        default:
-                                assert_not_reached("Invalid wake-up pointer");
-                        }
-                }
-                if (r < 0)
-                        goto finish;
-        }
-
-        r = process_watchdog(e);
-        if (r < 0)
-                goto finish;
-
-        r = process_timer(e, e->timestamp.realtime, &e->realtime);
-        if (r < 0)
-                goto finish;
-
-        r = process_timer(e, e->timestamp.boottime, &e->boottime);
-        if (r < 0)
-                goto finish;
-
-        r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
-        if (r < 0)
-                goto finish;
-
-        r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm);
-        if (r < 0)
-                goto finish;
-
-        r = process_timer(e, e->timestamp.boottime, &e->boottime_alarm);
-        if (r < 0)
-                goto finish;
-
-        if (e->need_process_child) {
-                r = process_child(e);
-                if (r < 0)
-                        goto finish;
-        }
-
-        r = process_inotify(e);
-        if (r < 0)
-                goto finish;
-
-        if (event_next_pending(e)) {
-                e->state = SD_EVENT_PENDING;
-
-                return 1;
-        }
-
-        r = 0;
-
-finish:
-        e->state = SD_EVENT_INITIAL;
-
-        return r;
-}
-
-_public_ int sd_event_dispatch(sd_event *e) {
-        sd_event_source *p;
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(e->state == SD_EVENT_PENDING, -EBUSY);
-
-        if (e->exit_requested)
-                return dispatch_exit(e);
-
-        p = event_next_pending(e);
-        if (p) {
-                _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
-
-                e->state = SD_EVENT_RUNNING;
-                r = source_dispatch(p);
-                e->state = SD_EVENT_INITIAL;
-                return r;
-        }
-
-        e->state = SD_EVENT_INITIAL;
-
-        return 1;
-}
-
-static void event_log_delays(sd_event *e) {
-        char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1], *p;
-        size_t l, i;
-
-        p = b;
-        l = sizeof(b);
-        for (i = 0; i < ELEMENTSOF(e->delays); i++) {
-                l = strpcpyf(&p, l, "%u ", e->delays[i]);
-                e->delays[i] = 0;
-        }
-        log_debug("Event loop iterations: %s", b);
-}
-
-_public_ int sd_event_run(sd_event *e, uint64_t timeout) {
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
-
-        if (e->profile_delays && e->last_run_usec != 0) {
-                usec_t this_run;
-                unsigned l;
-
-                this_run = now(CLOCK_MONOTONIC);
-
-                l = u64log2(this_run - e->last_run_usec);
-                assert(l < ELEMENTSOF(e->delays));
-                e->delays[l]++;
-
-                if (this_run - e->last_log_usec >= 5*USEC_PER_SEC) {
-                        event_log_delays(e);
-                        e->last_log_usec = this_run;
-                }
-        }
-
-        /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
-
-        r = sd_event_prepare(e);
-        if (r == 0)
-                /* There was nothing? Then wait... */
-                r = sd_event_wait(e, timeout);
-
-        if (e->profile_delays)
-                e->last_run_usec = now(CLOCK_MONOTONIC);
-
-        if (r > 0) {
-                /* There's something now, then let's dispatch it */
-                r = sd_event_dispatch(e);
-                if (r < 0)
-                        return r;
-
-                return 1;
-        }
-
-        return r;
-}
-
-_public_ int sd_event_loop(sd_event *e) {
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
-
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
-
-        while (e->state != SD_EVENT_FINISHED) {
-                r = sd_event_run(e, (uint64_t) -1);
-                if (r < 0)
-                        return r;
-        }
-
-        return e->exit_code;
-}
-
-_public_ int sd_event_get_fd(sd_event *e) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        return e->epoll_fd;
-}
-
-_public_ int sd_event_get_state(sd_event *e) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        return e->state;
-}
-
-_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(code, -EINVAL);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!e->exit_requested)
-                return -ENODATA;
-
-        *code = e->exit_code;
-        return 0;
-}
-
-_public_ int sd_event_exit(sd_event *e, int code) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        e->exit_requested = true;
-        e->exit_code = code;
-
-        return 0;
-}
-
-_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(usec, -EINVAL);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock))
-                return -EOPNOTSUPP;
-
-        /* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here,
-         * for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for
-         * the purpose of getting the time this doesn't matter. */
-        if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported())
-                return -EOPNOTSUPP;
-
-        if (!triple_timestamp_is_set(&e->timestamp)) {
-                /* Implicitly fall back to now() if we never ran before and thus have no cached time. */
-                *usec = now(clock);
-                return 1;
-        }
-
-        *usec = triple_timestamp_by_clock(&e->timestamp, clock);
-        return 0;
-}
-
-_public_ int sd_event_default(sd_event **ret) {
-        sd_event *e = NULL;
-        int r;
-
-        if (!ret)
-                return !!default_event;
-
-        if (default_event) {
-                *ret = sd_event_ref(default_event);
-                return 0;
-        }
-
-        r = sd_event_new(&e);
-        if (r < 0)
-                return r;
-
-        e->default_event_ptr = &default_event;
-        e->tid = gettid();
-        default_event = e;
-
-        *ret = e;
-        return 1;
-}
-
-#if 0 /* NM_IGNORED */
-_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(tid, -EINVAL);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (e->tid != 0) {
-                *tid = e->tid;
-                return 0;
-        }
-
-        return -ENXIO;
-}
-
-_public_ int sd_event_set_watchdog(sd_event *e, int b) {
-        int r;
-
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        if (e->watchdog == !!b)
-                return e->watchdog;
-
-        if (b) {
-                r = sd_watchdog_enabled(false, &e->watchdog_period);
-                if (r <= 0)
-                        return r;
-
-                /* Issue first ping immediately */
-                sd_notify(false, "WATCHDOG=1");
-                e->watchdog_last = now(CLOCK_MONOTONIC);
-
-                e->watchdog_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
-                if (e->watchdog_fd < 0)
-                        return -errno;
-
-                r = arm_watchdog(e);
-                if (r < 0)
-                        goto fail;
-
-                struct epoll_event ev = {
-                        .events = EPOLLIN,
-                        .data.ptr = INT_TO_PTR(SOURCE_WATCHDOG),
-                };
-
-                if (epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
-
-        } else {
-                if (e->watchdog_fd >= 0) {
-                        (void) epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL);
-                        e->watchdog_fd = safe_close(e->watchdog_fd);
-                }
-        }
-
-        e->watchdog = !!b;
-        return e->watchdog;
-
-fail:
-        e->watchdog_fd = safe_close(e->watchdog_fd);
-        return r;
-}
-
-_public_ int sd_event_get_watchdog(sd_event *e) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        return e->watchdog;
-}
-
-_public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) {
-        assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(!event_pid_changed(e), -ECHILD);
-
-        *ret = e->iteration;
-        return 0;
-}
-
-_public_ int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback) {
-        assert_return(s, -EINVAL);
-
-        s->destroy_callback = callback;
-        return 0;
-}
-
-_public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret) {
-        assert_return(s, -EINVAL);
-
-        if (ret)
-                *ret = s->destroy_callback;
-
-        return !!s->destroy_callback;
-}
-
-_public_ int sd_event_source_get_floating(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-
-        return s->floating;
-}
-
-_public_ int sd_event_source_set_floating(sd_event_source *s, int b) {
-        assert_return(s, -EINVAL);
-
-        if (s->floating == !!b)
-                return 0;
-
-        if (!s->event) /* Already disconnected */
-                return -ESTALE;
-
-        s->floating = b;
-
-        if (b) {
-                sd_event_source_ref(s);
-                sd_event_unref(s->event);
-        } else {
-                sd_event_ref(s->event);
-                sd_event_source_unref(s);
-        }
-
-        return 1;
-}
-
-_public_ int sd_event_source_get_exit_on_failure(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type != SOURCE_EXIT, -EDOM);
-
-        return s->exit_on_failure;
-}
-
-_public_ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type != SOURCE_EXIT, -EDOM);
-
-        if (s->exit_on_failure == !!b)
-                return 0;
-
-        s->exit_on_failure = b;
-        return 1;
-}
-
-_public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval, unsigned burst) {
-        int r;
-
-        assert_return(s, -EINVAL);
-
-        /* Turning on ratelimiting on event source types that don't support it, is a loggable offense. Doing
-         * so is a programming error. */
-        assert_return(EVENT_SOURCE_CAN_RATE_LIMIT(s->type), -EDOM);
-
-        /* When ratelimiting is configured we'll always reset the rate limit state first and start fresh,
-         * non-ratelimited. */
-        r = event_source_leave_ratelimit(s);
-        if (r < 0)
-                return r;
-
-        s->rate_limit = (RateLimit) { interval, burst };
-        return 0;
-}
-
-_public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) {
-        assert_return(s, -EINVAL);
-
-        /* Querying whether an event source has ratelimiting configured is not a loggable offsense, hence
-         * don't use assert_return(). Unlike turning on ratelimiting it's not really a programming error */
-        if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type))
-                return -EDOM;
-
-        if (!ratelimit_configured(&s->rate_limit))
-                return -ENOEXEC;
-
-        if (ret_interval)
-                *ret_interval = s->rate_limit.interval;
-        if (ret_burst)
-                *ret_burst = s->rate_limit.burst;
-
-        return 0;
-}
-
-_public_ int sd_event_source_is_ratelimited(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-
-        if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type))
-                return false;
-
-        if (!ratelimit_configured(&s->rate_limit))
-                return false;
-
-        return s->ratelimited;
-}
-#endif /* NM_IGNORED */
diff --git a/src/core/systemd/src/libsystemd/sd-id128/id128-util.c b/src/core/systemd/src/libsystemd/sd-id128/id128-util.c
deleted file mode 100644
index b61cfdb0..00000000
--- a/src/core/systemd/src/libsystemd/sd-id128/id128-util.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "fd-util.h"
-#include "fs-util.h"
-#include "hexdecoct.h"
-#include "id128-util.h"
-#include "io-util.h"
-#include "stdio-util.h"
-#include "string-util.h"
-
-#if 0 /* NM_IGNORED */
-char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
-        unsigned n, k = 0;
-
-        assert(s);
-
-        /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
-
-        for (n = 0; n < 16; n++) {
-
-                if (IN_SET(n, 4, 6, 8, 10))
-                        s[k++] = '-';
-
-                s[k++] = hexchar(id.bytes[n] >> 4);
-                s[k++] = hexchar(id.bytes[n] & 0xF);
-        }
-
-        assert(k == 36);
-
-        s[k] = 0;
-
-        return s;
-}
-
-bool id128_is_valid(const char *s) {
-        size_t i, l;
-
-        assert(s);
-
-        l = strlen(s);
-        if (l == 32) {
-
-                /* Plain formatted 128bit hex string */
-
-                for (i = 0; i < l; i++) {
-                        char c = s[i];
-
-                        if (!(c >= '0' && c <= '9') &&
-                            !(c >= 'a' && c <= 'z') &&
-                            !(c >= 'A' && c <= 'Z'))
-                                return false;
-                }
-
-        } else if (l == 36) {
-
-                /* Formatted UUID */
-
-                for (i = 0; i < l; i++) {
-                        char c = s[i];
-
-                        if (IN_SET(i, 8, 13, 18, 23)) {
-                                if (c != '-')
-                                        return false;
-                        } else {
-                                if (!(c >= '0' && c <= '9') &&
-                                    !(c >= 'a' && c <= 'z') &&
-                                    !(c >= 'A' && c <= 'Z'))
-                                        return false;
-                        }
-                }
-
-        } else
-                return false;
-
-        return true;
-}
-#endif /* NM_IGNORED */
-
-int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
-        char buffer[36 + 2];
-        ssize_t l;
-
-        assert(fd >= 0);
-        assert(f < _ID128_FORMAT_MAX);
-
-        /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
-         * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
-         * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
-         * accept". */
-
-        l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
-        if (l < 0)
-                return (int) l;
-        if (l == 0) /* empty? */
-                return -ENOMEDIUM;
-
-        switch (l) {
-
-        case 13:
-        case 14:
-                /* Treat an "uninitialized" id file like an empty one */
-                return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL;
-
-        case 33: /* plain UUID with trailing newline */
-                if (buffer[32] != '\n')
-                        return -EINVAL;
-
-                _fallthrough_;
-        case 32: /* plain UUID without trailing newline */
-                if (f == ID128_UUID)
-                        return -EINVAL;
-
-                buffer[32] = 0;
-                break;
-
-        case 37: /* RFC UUID with trailing newline */
-                if (buffer[36] != '\n')
-                        return -EINVAL;
-
-                _fallthrough_;
-        case 36: /* RFC UUID without trailing newline */
-                if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
-                        return -EINVAL;
-
-                buffer[36] = 0;
-                break;
-
-        default:
-                return -EINVAL;
-        }
-
-        return sd_id128_from_string(buffer, ret);
-}
-
-int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
-        _cleanup_close_ int fd = -1;
-
-        fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return -errno;
-
-        return id128_read_fd(fd, f, ret);
-}
-
-#if 0 /* NM_IGNORED */
-int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
-        char buffer[36 + 2];
-        size_t sz;
-        int r;
-
-        assert(fd >= 0);
-        assert(f < _ID128_FORMAT_MAX);
-
-        if (f != ID128_UUID) {
-                sd_id128_to_string(id, buffer);
-                buffer[32] = '\n';
-                sz = 33;
-        } else {
-                id128_to_uuid_string(id, buffer);
-                buffer[36] = '\n';
-                sz = 37;
-        }
-
-        r = loop_write(fd, buffer, sz, false);
-        if (r < 0)
-                return r;
-
-        if (do_sync) {
-                if (fsync(fd) < 0)
-                        return -errno;
-
-                r = fsync_directory_of_file(fd);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
-        _cleanup_close_ int fd = -1;
-
-        fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
-        if (fd < 0)
-                return -errno;
-
-        return id128_write_fd(fd, f, id, do_sync);
-}
-
-void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
-        siphash24_compress(p, sizeof(sd_id128_t), state);
-}
-
-int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
-        return memcmp(a, b, 16);
-}
-
-sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
-        /* Stolen from generate_random_uuid() of drivers/char/random.c
-         * in the kernel sources */
-
-        /* Set UUID version to 4 --- truly random generation */
-        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
-
-        /* Set the UUID variant to DCE */
-        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
-
-        return id;
-}
-
-DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
-#endif /* NM_IGNORED */
diff --git a/src/core/systemd/src/libsystemd/sd-id128/id128-util.h b/src/core/systemd/src/libsystemd/sd-id128/id128-util.h
deleted file mode 100644
index 6b09bcd9..00000000
--- a/src/core/systemd/src/libsystemd/sd-id128/id128-util.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <stdbool.h>
-
-#include "sd-id128.h"
-
-#include "hash-funcs.h"
-#include "macro.h"
-
-#define ID128_UUID_STRING_MAX 37
-
-char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]);
-
-bool id128_is_valid(const char *s) _pure_;
-
-typedef enum Id128Format {
-        ID128_ANY,
-        ID128_PLAIN,  /* formatted as 32 hex chars as-is */
-        ID128_PLAIN_OR_UNINIT,  /* formatted as 32 hex chars as-is; allow special "uninitialized"
-                                 * value when reading from file (id128_read() and id128_read_fd()).
-                                 *
-                                 * This format should be used when reading a machine-id file. */
-        ID128_UUID,   /* formatted as 36 character uuid string */
-        _ID128_FORMAT_MAX,
-} Id128Format;
-
-int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret);
-int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
-
-int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
-int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
-
-void id128_hash_func(const sd_id128_t *p, struct siphash *state);
-int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
-extern const struct hash_ops id128_hash_ops;
-
-sd_id128_t id128_make_v4_uuid(sd_id128_t id);
diff --git a/src/core/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/core/systemd/src/libsystemd/sd-id128/sd-id128.c
deleted file mode 100644
index 64b8c787..00000000
--- a/src/core/systemd/src/libsystemd/sd-id128/sd-id128.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "sd-id128.h"
-
-#include "alloc-util.h"
-#include "fd-util.h"
-#include "hexdecoct.h"
-#include "id128-util.h"
-#include "io-util.h"
-#include "khash.h"
-#include "macro.h"
-#include "missing_syscall.h"
-#include "random-util.h"
-#include "user-util.h"
-#include "util.h"
-
-#if 0 /* NM_IGNORED */
-_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
-        unsigned n;
-
-        assert_return(s, NULL);
-
-        for (n = 0; n < 16; n++) {
-                s[n*2] = hexchar(id.bytes[n] >> 4);
-                s[n*2+1] = hexchar(id.bytes[n] & 0xF);
-        }
-
-        s[32] = 0;
-
-        return s;
-}
-#endif /* NM_IGNORED */
-
-_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
-        unsigned n, i;
-        sd_id128_t t;
-        bool is_guid = false;
-
-        assert_return(s, -EINVAL);
-
-        for (n = 0, i = 0; n < 16;) {
-                int a, b;
-
-                if (s[i] == '-') {
-                        /* Is this a GUID? Then be nice, and skip over
-                         * the dashes */
-
-                        if (i == 8)
-                                is_guid = true;
-                        else if (IN_SET(i, 13, 18, 23)) {
-                                if (!is_guid)
-                                        return -EINVAL;
-                        } else
-                                return -EINVAL;
-
-                        i++;
-                        continue;
-                }
-
-                a = unhexchar(s[i++]);
-                if (a < 0)
-                        return -EINVAL;
-
-                b = unhexchar(s[i++]);
-                if (b < 0)
-                        return -EINVAL;
-
-                t.bytes[n++] = (a << 4) | b;
-        }
-
-        if (i != (is_guid ? 36 : 32))
-                return -EINVAL;
-
-        if (s[i] != 0)
-                return -EINVAL;
-
-        if (ret)
-                *ret = t;
-        return 0;
-}
-
-_public_ int sd_id128_get_machine(sd_id128_t *ret) {
-        static thread_local sd_id128_t saved_machine_id = {};
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        if (sd_id128_is_null(saved_machine_id)) {
-                r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
-                if (r < 0)
-                        return r;
-
-                if (sd_id128_is_null(saved_machine_id))
-                        return -ENOMEDIUM;
-        }
-
-        *ret = saved_machine_id;
-        return 0;
-}
-
-_public_ int sd_id128_get_boot(sd_id128_t *ret) {
-        static thread_local sd_id128_t saved_boot_id = {};
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        if (sd_id128_is_null(saved_boot_id)) {
-                r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret = saved_boot_id;
-        return 0;
-}
-
-#if 0 /* NM_IGNORED */
-static int get_invocation_from_keyring(sd_id128_t *ret) {
-        _cleanup_free_ char *description = NULL;
-        char *d, *p, *g, *u, *e;
-        unsigned long perms;
-        key_serial_t key;
-        size_t sz = 256;
-        uid_t uid;
-        gid_t gid;
-        int r, c;
-
-#define MAX_PERMS ((unsigned long) (KEY_POS_VIEW|KEY_POS_READ|KEY_POS_SEARCH| \
-                                    KEY_USR_VIEW|KEY_USR_READ|KEY_USR_SEARCH))
-
-        assert(ret);
-
-        key = request_key("user", "invocation_id", NULL, 0);
-        if (key == -1) {
-                /* Keyring support not available? No invocation key stored? */
-                if (IN_SET(errno, ENOSYS, ENOKEY))
-                        return -ENXIO;
-
-                return -errno;
-        }
-
-        for (;;) {
-                description = new(char, sz);
-                if (!description)
-                        return -ENOMEM;
-
-                c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
-                if (c < 0)
-                        return -errno;
-
-                if ((size_t) c <= sz)
-                        break;
-
-                sz = c;
-                free(description);
-        }
-
-        /* The kernel returns a final NUL in the string, verify that. */
-        assert(description[c-1] == 0);
-
-        /* Chop off the final description string */
-        d = strrchr(description, ';');
-        if (!d)
-                return -EIO;
-        *d = 0;
-
-        /* Look for the permissions */
-        p = strrchr(description, ';');
-        if (!p)
-                return -EIO;
-
-        errno = 0;
-        perms = strtoul(p + 1, &e, 16);
-        if (errno > 0)
-                return -errno;
-        if (e == p + 1) /* Read at least one character */
-                return -EIO;
-        if (e != d) /* Must reached the end */
-                return -EIO;
-
-        if ((perms & ~MAX_PERMS) != 0)
-                return -EPERM;
-
-        *p = 0;
-
-        /* Look for the group ID */
-        g = strrchr(description, ';');
-        if (!g)
-                return -EIO;
-        r = parse_gid(g + 1, &gid);
-        if (r < 0)
-                return r;
-        if (gid != 0)
-                return -EPERM;
-        *g = 0;
-
-        /* Look for the user ID */
-        u = strrchr(description, ';');
-        if (!u)
-                return -EIO;
-        r = parse_uid(u + 1, &uid);
-        if (r < 0)
-                return r;
-        if (uid != 0)
-                return -EPERM;
-
-        c = keyctl(KEYCTL_READ, key, (unsigned long) ret, sizeof(sd_id128_t), 0);
-        if (c < 0)
-                return -errno;
-        if (c != sizeof(sd_id128_t))
-                return -EIO;
-
-        return 0;
-}
-
-static int get_invocation_from_environment(sd_id128_t *ret) {
-        const char *e;
-
-        assert(ret);
-
-        e = secure_getenv("INVOCATION_ID");
-        if (!e)
-                return -ENXIO;
-
-        return sd_id128_from_string(e, ret);
-}
-
-_public_ int sd_id128_get_invocation(sd_id128_t *ret) {
-        static thread_local sd_id128_t saved_invocation_id = {};
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        if (sd_id128_is_null(saved_invocation_id)) {
-                /* We first check the environment. The environment variable is primarily relevant for user
-                 * services, and sufficiently safe as long as no privilege boundary is involved. */
-                r = get_invocation_from_environment(&saved_invocation_id);
-                if (r < 0 && r != -ENXIO)
-                        return r;
-
-                /* The kernel keyring is relevant for system services (as for user services we don't store
-                 * the invocation ID in the keyring, as there'd be no trust benefit in that). */
-                r = get_invocation_from_keyring(&saved_invocation_id);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret = saved_invocation_id;
-        return 0;
-}
-
-_public_ int sd_id128_randomize(sd_id128_t *ret) {
-        sd_id128_t t;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be
-         * fine for UUIDS. */
-        r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND);
-        if (r < 0)
-                return r;
-
-        /* Turn this into a valid v4 UUID, to be nice. Note that we
-         * only guarantee this for newly generated UUIDs, not for
-         * pre-existing ones. */
-
-        *ret = id128_make_v4_uuid(t);
-        return 0;
-}
-
-static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
-        _cleanup_(khash_unrefp) khash *h = NULL;
-        sd_id128_t result;
-        const void *p;
-        int r;
-
-        assert(ret);
-
-        r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
-        if (r < 0)
-                return r;
-
-        r = khash_put(h, &app_id, sizeof(app_id));
-        if (r < 0)
-                return r;
-
-        r = khash_digest_data(h, &p);
-        if (r < 0)
-                return r;
-
-        /* We chop off the trailing 16 bytes */
-        memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
-
-        *ret = id128_make_v4_uuid(result);
-        return 0;
-}
-
-_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
-        sd_id128_t id;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        r = sd_id128_get_machine(&id);
-        if (r < 0)
-                return r;
-
-        return get_app_specific(id, app_id, ret);
-}
-
-_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
-        sd_id128_t id;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        r = sd_id128_get_boot(&id);
-        if (r < 0)
-                return r;
-
-        return get_app_specific(id, app_id, ret);
-}
-#endif /* NM_IGNORED */
diff --git a/src/core/systemd/src/systemd/_sd-common.h b/src/core/systemd/src/systemd/_sd-common.h
deleted file mode 100644
index e3de2ae5..00000000
--- a/src/core/systemd/src/systemd/_sd-common.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdcommonhfoo
-#define foosdcommonhfoo
-
-/***
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/* This is a private header; never even think of including this directly! */
-
-#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__)
-#  error "Do not include _sd-common.h directly; it is a private header."
-#endif
-
-typedef void (*_sd_destroy_t)(void *userdata);
-
-#ifndef _sd_printf_
-#  if __GNUC__ >= 4
-#    define _sd_printf_(a,b) __attribute__((__format__(printf, a, b)))
-#  else
-#    define _sd_printf_(a,b)
-#  endif
-#endif
-
-#ifndef _sd_sentinel_
-#  define _sd_sentinel_ __attribute__((__sentinel__))
-#endif
-
-#ifndef _sd_packed_
-#  define _sd_packed_ __attribute__((__packed__))
-#endif
-
-#ifndef _sd_pure_
-#  define _sd_pure_ __attribute__((__pure__))
-#endif
-
-/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
- * it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
- * (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
- * are probably going to use something newer anyway. */
-#ifndef _sd_deprecated_
-#  if __GNUC__ >= 6
-#    define _sd_deprecated_ __attribute__((__deprecated__))
-#  else
-#    define _sd_deprecated_
-#  endif
-#endif
-
-#ifndef _SD_STRINGIFY
-#  define _SD_XSTRINGIFY(x) #x
-#  define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x)
-#endif
-
-#ifndef _SD_BEGIN_DECLARATIONS
-#  ifdef __cplusplus
-#    define _SD_BEGIN_DECLARATIONS                              \
-        extern "C" {                                            \
-        struct _sd_useless_struct_to_allow_trailing_semicolon_
-#  else
-#    define _SD_BEGIN_DECLARATIONS                              \
-        struct _sd_useless_struct_to_allow_trailing_semicolon_
-#  endif
-#endif
-
-#ifndef _SD_END_DECLARATIONS
-#  ifdef __cplusplus
-#    define _SD_END_DECLARATIONS                                \
-        }                                                       \
-        struct _sd_useless_cpp_struct_to_allow_trailing_semicolon_
-#  else
-#    define _SD_END_DECLARATIONS                                \
-        struct _sd_useless_struct_to_allow_trailing_semicolon_
-#  endif
-#endif
-
-#ifndef _SD_ARRAY_STATIC
-#  if __STDC_VERSION__ >= 199901L
-#    define _SD_ARRAY_STATIC static
-#  else
-#    define _SD_ARRAY_STATIC
-#  endif
-#endif
-
-#define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func)             \
-        static __inline__ void func##p(type **p) {              \
-                if (*p)                                         \
-                        func(*p);                               \
-        }                                                       \
-        struct _sd_useless_struct_to_allow_trailing_semicolon_
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-dhcp-client.h b/src/core/systemd/src/systemd/sd-dhcp-client.h
deleted file mode 100644
index c35328a9..00000000
--- a/src/core/systemd/src/systemd/sd-dhcp-client.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosddhcpclienthfoo
-#define foosddhcpclienthfoo
-
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <sys/types.h>
-#include <stdbool.h>
-
-#include "sd-dhcp-lease.h"
-#include "sd-dhcp-option.h"
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-enum {
-        SD_DHCP_CLIENT_EVENT_STOP               = 0,
-        SD_DHCP_CLIENT_EVENT_IP_ACQUIRE         = 1,
-        SD_DHCP_CLIENT_EVENT_IP_CHANGE          = 2,
-        SD_DHCP_CLIENT_EVENT_EXPIRED            = 3,
-        SD_DHCP_CLIENT_EVENT_RENEW              = 4,
-        SD_DHCP_CLIENT_EVENT_SELECTING          = 5,
-        SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE  = 6, /* Sent when we have not received a reply after the first few attempts.
-                                                      * The client may want to start acquiring link-local addresses. */
-};
-
-enum {
-        SD_DHCP_OPTION_PAD                         = 0,
-        SD_DHCP_OPTION_SUBNET_MASK                 = 1,
-        SD_DHCP_OPTION_TIME_OFFSET                 = 2,
-        SD_DHCP_OPTION_ROUTER                      = 3,
-        SD_DHCP_OPTION_DOMAIN_NAME_SERVER          = 6,
-        SD_DHCP_OPTION_LPR_SERVER                  = 9,
-        SD_DHCP_OPTION_HOST_NAME                   = 12,
-        SD_DHCP_OPTION_BOOT_FILE_SIZE              = 13,
-        SD_DHCP_OPTION_DOMAIN_NAME                 = 15,
-        SD_DHCP_OPTION_ROOT_PATH                   = 17,
-        SD_DHCP_OPTION_ENABLE_IP_FORWARDING        = 19,
-        SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL     = 20,
-        SD_DHCP_OPTION_POLICY_FILTER               = 21,
-        SD_DHCP_OPTION_INTERFACE_MDR               = 22,
-        SD_DHCP_OPTION_INTERFACE_TTL               = 23,
-        SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
-        SD_DHCP_OPTION_INTERFACE_MTU               = 26,
-        SD_DHCP_OPTION_BROADCAST                   = 28,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_ROUTER_DISCOVER             = 31,
-        SD_DHCP_OPTION_STATIC_ROUTE                = 33,
-        SD_DHCP_OPTION_NTP_SERVER                  = 42,
-        SD_DHCP_OPTION_VENDOR_SPECIFIC             = 43,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_NETBIOS_NAMESERVER          = 44,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_NETBIOS_NODETYPE            = 46,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_NETBIOS_SCOPE               = 47,
-        SD_DHCP_OPTION_REQUESTED_IP_ADDRESS        = 50,
-        SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME       = 51,
-        SD_DHCP_OPTION_OVERLOAD                    = 52,
-        SD_DHCP_OPTION_MESSAGE_TYPE                = 53,
-        SD_DHCP_OPTION_SERVER_IDENTIFIER           = 54,
-        SD_DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
-        SD_DHCP_OPTION_ERROR_MESSAGE               = 56,
-        SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE        = 57,
-        SD_DHCP_OPTION_RENEWAL_T1_TIME             = 58,
-        SD_DHCP_OPTION_REBINDING_T2_TIME           = 59,
-        SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER     = 60,
-        SD_DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
-        SD_DHCP_OPTION_SMTP_SERVER                 = 69,
-        SD_DHCP_OPTION_POP3_SERVER                 = 70,
-        SD_DHCP_OPTION_USER_CLASS                  = 77,
-        SD_DHCP_OPTION_FQDN                        = 81,
-        SD_DHCP_OPTION_NEW_POSIX_TIMEZONE          = 100,
-        SD_DHCP_OPTION_NEW_TZDB_TIMEZONE           = 101,
-        SD_DHCP_OPTION_DOMAIN_SEARCH_LIST          = 119,
-        SD_DHCP_OPTION_SIP_SERVER                  = 120,
-        SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
-        SD_DHCP_OPTION_MUD_URL                     = 161,
-        SD_DHCP_OPTION_PRIVATE_BASE                = 224,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
-        SD_DHCP_OPTION_PRIVATE_LAST                = 254,
-        SD_DHCP_OPTION_END                         = 255,
-};
-
-typedef struct sd_dhcp_client sd_dhcp_client;
-
-typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata);
-int sd_dhcp_client_set_callback(
-                sd_dhcp_client *client,
-                sd_dhcp_client_callback_t cb,
-                void *userdata);
-
-int sd_dhcp_client_set_request_option(
-                sd_dhcp_client *client,
-                uint8_t option);
-int sd_dhcp_client_set_request_address(
-                sd_dhcp_client *client,
-                const struct in_addr *last_address);
-int sd_dhcp_client_set_request_broadcast(
-                sd_dhcp_client *client,
-                int broadcast);
-int sd_dhcp_client_set_ifindex(
-                sd_dhcp_client *client,
-                int interface_index);
-int sd_dhcp_client_set_mac(
-                sd_dhcp_client *client,
-                const uint8_t *addr,
-                const uint8_t *bcast_addr,
-                size_t addr_len,
-                uint16_t arp_type);
-int sd_dhcp_client_set_client_id(
-                sd_dhcp_client *client,
-                uint8_t type,
-                const uint8_t *data,
-                size_t data_len);
-int sd_dhcp_client_set_iaid_duid(
-                sd_dhcp_client *client,
-                bool iaid_set,
-                uint32_t iaid,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len);
-int sd_dhcp_client_set_iaid_duid_llt(
-                sd_dhcp_client *client,
-                bool iaid_set,
-                uint32_t iaid,
-                uint64_t llt_time);
-int sd_dhcp_client_set_duid(
-                sd_dhcp_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len);
-int sd_dhcp_client_set_duid_llt(
-                sd_dhcp_client *client,
-                uint64_t llt_time);
-int sd_dhcp_client_get_client_id(
-                sd_dhcp_client *client,
-                uint8_t *type,
-                const uint8_t **data,
-                size_t *data_len);
-int sd_dhcp_client_set_mtu(
-                sd_dhcp_client *client,
-                uint32_t mtu);
-int sd_dhcp_client_set_max_attempts(
-                sd_dhcp_client *client,
-                uint64_t attempt);
-int sd_dhcp_client_set_client_port(
-                sd_dhcp_client *client,
-                uint16_t port);
-int sd_dhcp_client_set_hostname(
-                sd_dhcp_client *client,
-                const char *hostname);
-int sd_dhcp_client_set_vendor_class_identifier(
-                sd_dhcp_client *client,
-                const char *vci);
-int sd_dhcp_client_set_mud_url(
-                sd_dhcp_client *client,
-                const char *mudurl);
-int sd_dhcp_client_set_user_class(
-                sd_dhcp_client *client,
-                const char* const *user_class);
-int sd_dhcp_client_get_lease(
-                sd_dhcp_client *client,
-                sd_dhcp_lease **ret);
-int sd_dhcp_client_set_service_type(
-                sd_dhcp_client *client,
-                int type);
-int sd_dhcp_client_set_fallback_lease_lifetime(
-                sd_dhcp_client *client,
-                uint32_t fallback_lease_lifetime);
-
-int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
-int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
-
-int sd_dhcp_client_stop(sd_dhcp_client *client);
-int sd_dhcp_client_start(sd_dhcp_client *client);
-int sd_dhcp_client_send_release(sd_dhcp_client *client);
-int sd_dhcp_client_send_decline(sd_dhcp_client *client);
-int sd_dhcp_client_send_renew(sd_dhcp_client *client);
-
-sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);
-sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
-
-/* NOTE: anonymize parameter is used to initialize PRL memory with different
- * options when using RFC7844 Anonymity Profiles */
-int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
-
-int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret);
-
-int sd_dhcp_client_attach_event(
-                sd_dhcp_client *client,
-                sd_event *event,
-                int64_t priority);
-int sd_dhcp_client_detach_event(sd_dhcp_client *client);
-sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_client, sd_dhcp_client_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-dhcp-lease.h b/src/core/systemd/src/systemd/sd-dhcp-lease.h
deleted file mode 100644
index c255a1f9..00000000
--- a/src/core/systemd/src/systemd/sd-dhcp-lease.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosddhcpleasehfoo
-#define foosddhcpleasehfoo
-
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <sys/types.h>
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-typedef struct sd_dhcp_lease sd_dhcp_lease;
-typedef struct sd_dhcp_route sd_dhcp_route;
-
-sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
-sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
-
-typedef enum sd_dhcp_lease_server_type {
-        SD_DHCP_LEASE_DNS,
-        SD_DHCP_LEASE_NTP,
-        SD_DHCP_LEASE_SIP,
-        SD_DHCP_LEASE_POP3,
-        SD_DHCP_LEASE_SMTP,
-        SD_DHCP_LEASE_LPR,
-        _SD_DHCP_LEASE_SERVER_TYPE_MAX,
-        _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1,
-} sd_dhcp_lease_server_type;
-
-int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
-int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
-int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2);
-int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr);
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
-int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
-int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
-int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
-int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
-
-int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
-int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
-int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
-int sd_dhcp_route_get_option(sd_dhcp_route *route);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-dhcp-option.h b/src/core/systemd/src/systemd/sd-dhcp-option.h
deleted file mode 100644
index 71aa479b..00000000
--- a/src/core/systemd/src/systemd/sd-dhcp-option.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosddhcpoptionhfoo
-#define foosddhcpoptionhfoo
-
-/***
-  Copyright © 2013 Intel Corporation. All rights reserved.
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <sys/types.h>
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-typedef struct sd_dhcp_option sd_dhcp_option;
-
-int sd_dhcp_option_new(uint8_t option, const void *data, size_t length, sd_dhcp_option **ret);
-sd_dhcp_option *sd_dhcp_option_ref(sd_dhcp_option *ra);
-sd_dhcp_option *sd_dhcp_option_unref(sd_dhcp_option *ra);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_option, sd_dhcp_option_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-dhcp6-client.h b/src/core/systemd/src/systemd/sd-dhcp6-client.h
deleted file mode 100644
index 75ee27d6..00000000
--- a/src/core/systemd/src/systemd/sd-dhcp6-client.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosddhcp6clienthfoo
-#define foosddhcp6clienthfoo
-
-/***
-  Copyright © 2014 Intel Corporation. All rights reserved.
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <net/ethernet.h>
-#include <sys/types.h>
-
-#include "sd-dhcp6-lease.h"
-#include "sd-dhcp6-option.h"
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-enum {
-        SD_DHCP6_CLIENT_EVENT_STOP                      = 0,
-        SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE             = 10,
-        SD_DHCP6_CLIENT_EVENT_RETRANS_MAX               = 11,
-        SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE                = 12,
-        SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST       = 13,
-};
-
-enum {
-        SD_DHCP6_OPTION_CLIENTID                   = 1,
-        SD_DHCP6_OPTION_SERVERID                   = 2,
-        SD_DHCP6_OPTION_IA_NA                      = 3,
-        SD_DHCP6_OPTION_IA_TA                      = 4,
-        SD_DHCP6_OPTION_IAADDR                     = 5,
-        SD_DHCP6_OPTION_ORO                        = 6,
-        SD_DHCP6_OPTION_PREFERENCE                 = 7,
-        SD_DHCP6_OPTION_ELAPSED_TIME               = 8,
-        SD_DHCP6_OPTION_RELAY_MSG                  = 9,
-        /* option code 10 is unassigned */
-        SD_DHCP6_OPTION_AUTH                       = 11,
-        SD_DHCP6_OPTION_UNICAST                    = 12,
-        SD_DHCP6_OPTION_STATUS_CODE                = 13,
-        SD_DHCP6_OPTION_RAPID_COMMIT               = 14,
-        SD_DHCP6_OPTION_USER_CLASS                 = 15,
-        SD_DHCP6_OPTION_VENDOR_CLASS               = 16,
-        SD_DHCP6_OPTION_VENDOR_OPTS                = 17,
-        SD_DHCP6_OPTION_INTERFACE_ID               = 18,
-        SD_DHCP6_OPTION_RECONF_MSG                 = 19,
-        SD_DHCP6_OPTION_RECONF_ACCEPT              = 20,
-
-        SD_DHCP6_OPTION_DNS_SERVERS                = 23,  /* RFC 3646 */
-        SD_DHCP6_OPTION_DOMAIN_LIST                = 24,  /* RFC 3646 */
-        SD_DHCP6_OPTION_IA_PD                      = 25,  /* RFC 3633, prefix delegation */
-        SD_DHCP6_OPTION_IA_PD_PREFIX               = 26,  /* RFC 3633, prefix delegation */
-
-        SD_DHCP6_OPTION_SNTP_SERVERS               = 31,  /* RFC 4075, deprecated */
-        SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME   = 32,  /* RFC 8415, sec. 21.23 */
-
-        /* option code 35 is unassigned */
-
-        SD_DHCP6_OPTION_FQDN                       = 39,  /* RFC 4704 */
-
-        SD_DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
-        SD_DHCP6_OPTION_MUD_URL                    = 112, /* RFC 8250 */
-
-        /* option codes 89-142 are unassigned */
-        /* option codes 144-65535 are unassigned */
-};
-
-typedef struct sd_dhcp6_client sd_dhcp6_client;
-
-typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event, void *userdata);
-int sd_dhcp6_client_set_callback(
-                sd_dhcp6_client *client,
-                sd_dhcp6_client_callback_t cb,
-                void *userdata);
-
-int sd_dhcp6_client_set_ifindex(
-                sd_dhcp6_client *client,
-                int interface_index);
-int sd_dhcp6_client_set_local_address(
-                sd_dhcp6_client *client,
-                const struct in6_addr *local_address);
-int sd_dhcp6_client_set_mac(
-                sd_dhcp6_client *client,
-                const uint8_t *addr,
-                size_t addr_len,
-                uint16_t arp_type);
-int sd_dhcp6_client_set_duid(
-                sd_dhcp6_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len);
-int sd_dhcp6_client_set_duid_llt(
-                sd_dhcp6_client *client,
-                uint64_t llt_time);
-int sd_dhcp6_client_set_iaid(
-                sd_dhcp6_client *client,
-                uint32_t iaid);
-int sd_dhcp6_client_get_iaid(
-                sd_dhcp6_client *client,
-                uint32_t *iaid);
-int sd_dhcp6_client_duid_as_string(
-                sd_dhcp6_client *client,
-                char **duid);
-int sd_dhcp6_client_set_fqdn(
-                sd_dhcp6_client *client,
-                const char *fqdn);
-int sd_dhcp6_client_set_information_request(
-                sd_dhcp6_client *client,
-                int enabled);
-int sd_dhcp6_client_get_information_request(
-                sd_dhcp6_client *client,
-                int *enabled);
-int sd_dhcp6_client_set_request_option(
-                sd_dhcp6_client *client,
-                uint16_t option);
-int sd_dhcp6_client_set_request_mud_url(
-                sd_dhcp6_client *client,
-                const char *mudurl);
-int sd_dhcp6_client_set_request_user_class(
-                sd_dhcp6_client *client,
-                char** user_class);
-int sd_dhcp6_client_set_request_vendor_class(
-                sd_dhcp6_client *client,
-                char** vendor_class);
-int sd_dhcp6_client_set_prefix_delegation_hint(
-                sd_dhcp6_client *client,
-                uint8_t prefixlen,
-                const struct in6_addr *pd_address);
-int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
-                                          int *delegation);
-int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
-                                          int delegation);
-int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
-                                        int *request);
-int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
-                                        int request);
-int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
-                                       uint32_t transaction_id);
-int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
-                                      sd_dhcp6_option *v);
-
-int sd_dhcp6_client_get_lease(
-                sd_dhcp6_client *client,
-                sd_dhcp6_lease **ret);
-
-int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v);
-
-int sd_dhcp6_client_stop(sd_dhcp6_client *client);
-int sd_dhcp6_client_start(sd_dhcp6_client *client);
-int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
-int sd_dhcp6_client_attach_event(
-                sd_dhcp6_client *client,
-                sd_event *event,
-                int64_t priority);
-int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
-sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client);
-sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);
-sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client);
-int sd_dhcp6_client_new(sd_dhcp6_client **ret);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_client, sd_dhcp6_client_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-dhcp6-lease.h b/src/core/systemd/src/systemd/sd-dhcp6-lease.h
deleted file mode 100644
index f77b31ac..00000000
--- a/src/core/systemd/src/systemd/sd-dhcp6-lease.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosddhcp6leasehfoo
-#define foosddhcp6leasehfoo
-
-/***
-  Copyright © 2014-2015 Intel Corporation. All rights reserved.
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <netinet/in.h>
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-typedef struct sd_dhcp6_lease sd_dhcp6_lease;
-
-void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
-int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
-                               struct in6_addr *addr,
-                               uint32_t *lifetime_preferred,
-                               uint32_t *lifetime_valid);
-void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
-int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
-                          uint8_t *prefix_len,
-                          uint32_t *lifetime_preferred,
-                          uint32_t *lifetime_valid);
-
-int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
-int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
-int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
-int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
-int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn);
-
-sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
-sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_lease, sd_dhcp6_lease_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-dhcp6-option.h b/src/core/systemd/src/systemd/sd-dhcp6-option.h
deleted file mode 100644
index ddb2c7ce..00000000
--- a/src/core/systemd/src/systemd/sd-dhcp6-option.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosddhcp6optionhfoo
-#define foosddhcp6optionhfoo
-
-/***
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <sys/types.h>
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-typedef struct sd_dhcp6_option sd_dhcp6_option;
-
-int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret);
-sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra);
-sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_option, sd_dhcp6_option_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-event.h b/src/core/systemd/src/systemd/sd-event.h
deleted file mode 100644
index 2ae2a0da..00000000
--- a/src/core/systemd/src/systemd/sd-event.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdeventhfoo
-#define foosdeventhfoo
-
-/***
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <signal.h>
-#include <sys/epoll.h>
-#include <sys/inotify.h>
-#include <sys/signalfd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <time.h>
-
-#include "_sd-common.h"
-
-/*
-  Why is this better than pure epoll?
-
-  - Supports event source prioritization
-  - Scales better with a large number of time events because it does not require one timerfd each
-  - Automatically tries to coalesce timer events system-wide
-  - Handles signals, child PIDs, inotify events
-  - Supports systemd-style automatic watchdog event generation
-*/
-
-_SD_BEGIN_DECLARATIONS;
-
-#define SD_EVENT_DEFAULT ((sd_event *) 1)
-
-typedef struct sd_event sd_event;
-typedef struct sd_event_source sd_event_source;
-
-enum {
-        SD_EVENT_OFF = 0,
-        SD_EVENT_ON = 1,
-        SD_EVENT_ONESHOT = -1
-};
-
-enum {
-        SD_EVENT_INITIAL,
-        SD_EVENT_ARMED,
-        SD_EVENT_PENDING,
-        SD_EVENT_RUNNING,
-        SD_EVENT_EXITING,
-        SD_EVENT_FINISHED,
-        SD_EVENT_PREPARING
-};
-
-enum {
-        /* And everything in-between and outside is good too */
-        SD_EVENT_PRIORITY_IMPORTANT = -100,
-        SD_EVENT_PRIORITY_NORMAL = 0,
-        SD_EVENT_PRIORITY_IDLE = 100
-};
-
-typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata);
-typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata);
-typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata);
-typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata);
-#if defined _GNU_SOURCE || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L)
-typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata);
-#else
-typedef void* sd_event_child_handler_t;
-#endif
-typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata);
-typedef _sd_destroy_t sd_event_destroy_t;
-
-int sd_event_default(sd_event **e);
-
-int sd_event_new(sd_event **e);
-sd_event* sd_event_ref(sd_event *e);
-sd_event* sd_event_unref(sd_event *e);
-
-int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata);
-int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata);
-int sd_event_add_time_relative(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata);
-int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata);
-int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
-int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata);
-int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
-int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
-int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
-int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
-
-int sd_event_prepare(sd_event *e);
-int sd_event_wait(sd_event *e, uint64_t usec);
-int sd_event_dispatch(sd_event *e);
-int sd_event_run(sd_event *e, uint64_t usec);
-int sd_event_loop(sd_event *e);
-int sd_event_exit(sd_event *e, int code);
-
-int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec);
-
-int sd_event_get_fd(sd_event *e);
-int sd_event_get_state(sd_event *e);
-int sd_event_get_tid(sd_event *e, pid_t *tid);
-int sd_event_get_exit_code(sd_event *e, int *code);
-int sd_event_set_watchdog(sd_event *e, int b);
-int sd_event_get_watchdog(sd_event *e);
-int sd_event_get_iteration(sd_event *e, uint64_t *ret);
-
-sd_event_source* sd_event_source_ref(sd_event_source *s);
-sd_event_source* sd_event_source_unref(sd_event_source *s);
-sd_event_source* sd_event_source_disable_unref(sd_event_source *s);
-
-sd_event *sd_event_source_get_event(sd_event_source *s);
-void* sd_event_source_get_userdata(sd_event_source *s);
-void* sd_event_source_set_userdata(sd_event_source *s, void *userdata);
-
-int sd_event_source_set_description(sd_event_source *s, const char *description);
-int sd_event_source_get_description(sd_event_source *s, const char **description);
-int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback);
-int sd_event_source_get_pending(sd_event_source *s);
-int sd_event_source_get_priority(sd_event_source *s, int64_t *priority);
-int sd_event_source_set_priority(sd_event_source *s, int64_t priority);
-int sd_event_source_get_enabled(sd_event_source *s, int *enabled);
-int sd_event_source_set_enabled(sd_event_source *s, int enabled);
-int sd_event_source_get_io_fd(sd_event_source *s);
-int sd_event_source_set_io_fd(sd_event_source *s, int fd);
-int sd_event_source_get_io_fd_own(sd_event_source *s);
-int sd_event_source_set_io_fd_own(sd_event_source *s, int own);
-int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events);
-int sd_event_source_set_io_events(sd_event_source *s, uint32_t events);
-int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents);
-int sd_event_source_get_time(sd_event_source *s, uint64_t *usec);
-int sd_event_source_set_time(sd_event_source *s, uint64_t usec);
-int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec);
-int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec);
-int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec);
-int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock);
-int sd_event_source_get_signal(sd_event_source *s);
-int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
-int sd_event_source_get_child_pidfd(sd_event_source *s);
-int sd_event_source_get_child_pidfd_own(sd_event_source *s);
-int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own);
-int sd_event_source_get_child_process_own(sd_event_source *s);
-int sd_event_source_set_child_process_own(sd_event_source *s, int own);
-#if defined _GNU_SOURCE || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L)
-int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo_t *si, unsigned flags);
-#else
-int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags);
-#endif
-int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
-int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);
-int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret);
-int sd_event_source_get_floating(sd_event_source *s);
-int sd_event_source_set_floating(sd_event_source *s, int b);
-int sd_event_source_get_exit_on_failure(sd_event_source *s);
-int sd_event_source_set_exit_on_failure(sd_event_source *s, int b);
-int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst);
-int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst);
-int sd_event_source_is_ratelimited(sd_event_source *s);
-
-/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_disable_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-id128.h b/src/core/systemd/src/systemd/sd-id128.h
deleted file mode 100644
index 02aa318a..00000000
--- a/src/core/systemd/src/systemd/sd-id128.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdid128hfoo
-#define foosdid128hfoo
-
-/***
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <string.h>
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-/* 128-bit ID APIs. See sd-id128(3) for more information. */
-
-typedef union sd_id128 sd_id128_t;
-
-union sd_id128 {
-        uint8_t bytes[16];
-        uint64_t qwords[2];
-};
-
-#define SD_ID128_STRING_MAX 33
-
-char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
-int sd_id128_from_string(const char *s, sd_id128_t *ret);
-
-int sd_id128_randomize(sd_id128_t *ret);
-
-int sd_id128_get_machine(sd_id128_t *ret);
-int sd_id128_get_boot(sd_id128_t *ret);
-int sd_id128_get_invocation(sd_id128_t *ret);
-
-int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
-int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
-
-#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
-        { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
-                     0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}
-
-#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
-        ((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15))
-
-/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16
- * times. It is hence not a good idea to call this macro with an
- * expensive function as parameter or an expression with side
- * effects */
-
-#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
-#define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15]
-
-/* Like SD_ID128_FORMAT_STR, but formats as UUID, not in plain format */
-#define SD_ID128_UUID_FORMAT_STR "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
-
-#define SD_ID128_CONST_STR(x)                                           \
-        ((const char[SD_ID128_STRING_MAX]) {                            \
-                ((x).bytes[0] >> 4) >= 10 ? 'a' + ((x).bytes[0] >> 4) - 10 : '0' + ((x).bytes[0] >> 4), \
-                ((x).bytes[0] & 15) >= 10 ? 'a' + ((x).bytes[0] & 15) - 10 : '0' + ((x).bytes[0] & 15), \
-                ((x).bytes[1] >> 4) >= 10 ? 'a' + ((x).bytes[1] >> 4) - 10 : '0' + ((x).bytes[1] >> 4), \
-                ((x).bytes[1] & 15) >= 10 ? 'a' + ((x).bytes[1] & 15) - 10 : '0' + ((x).bytes[1] & 15), \
-                ((x).bytes[2] >> 4) >= 10 ? 'a' + ((x).bytes[2] >> 4) - 10 : '0' + ((x).bytes[2] >> 4), \
-                ((x).bytes[2] & 15) >= 10 ? 'a' + ((x).bytes[2] & 15) - 10 : '0' + ((x).bytes[2] & 15), \
-                ((x).bytes[3] >> 4) >= 10 ? 'a' + ((x).bytes[3] >> 4) - 10 : '0' + ((x).bytes[3] >> 4), \
-                ((x).bytes[3] & 15) >= 10 ? 'a' + ((x).bytes[3] & 15) - 10 : '0' + ((x).bytes[3] & 15), \
-                ((x).bytes[4] >> 4) >= 10 ? 'a' + ((x).bytes[4] >> 4) - 10 : '0' + ((x).bytes[4] >> 4), \
-                ((x).bytes[4] & 15) >= 10 ? 'a' + ((x).bytes[4] & 15) - 10 : '0' + ((x).bytes[4] & 15), \
-                ((x).bytes[5] >> 4) >= 10 ? 'a' + ((x).bytes[5] >> 4) - 10 : '0' + ((x).bytes[5] >> 4), \
-                ((x).bytes[5] & 15) >= 10 ? 'a' + ((x).bytes[5] & 15) - 10 : '0' + ((x).bytes[5] & 15), \
-                ((x).bytes[6] >> 4) >= 10 ? 'a' + ((x).bytes[6] >> 4) - 10 : '0' + ((x).bytes[6] >> 4), \
-                ((x).bytes[6] & 15) >= 10 ? 'a' + ((x).bytes[6] & 15) - 10 : '0' + ((x).bytes[6] & 15), \
-                ((x).bytes[7] >> 4) >= 10 ? 'a' + ((x).bytes[7] >> 4) - 10 : '0' + ((x).bytes[7] >> 4), \
-                ((x).bytes[7] & 15) >= 10 ? 'a' + ((x).bytes[7] & 15) - 10 : '0' + ((x).bytes[7] & 15), \
-                ((x).bytes[8] >> 4) >= 10 ? 'a' + ((x).bytes[8] >> 4) - 10 : '0' + ((x).bytes[8] >> 4), \
-                ((x).bytes[8] & 15) >= 10 ? 'a' + ((x).bytes[8] & 15) - 10 : '0' + ((x).bytes[8] & 15), \
-                ((x).bytes[9] >> 4) >= 10 ? 'a' + ((x).bytes[9] >> 4) - 10 : '0' + ((x).bytes[9] >> 4), \
-                ((x).bytes[9] & 15) >= 10 ? 'a' + ((x).bytes[9] & 15) - 10 : '0' + ((x).bytes[9] & 15), \
-                ((x).bytes[10] >> 4) >= 10 ? 'a' + ((x).bytes[10] >> 4) - 10 : '0' + ((x).bytes[10] >> 4), \
-                ((x).bytes[10] & 15) >= 10 ? 'a' + ((x).bytes[10] & 15) - 10 : '0' + ((x).bytes[10] & 15), \
-                ((x).bytes[11] >> 4) >= 10 ? 'a' + ((x).bytes[11] >> 4) - 10 : '0' + ((x).bytes[11] >> 4), \
-                ((x).bytes[11] & 15) >= 10 ? 'a' + ((x).bytes[11] & 15) - 10 : '0' + ((x).bytes[11] & 15), \
-                ((x).bytes[12] >> 4) >= 10 ? 'a' + ((x).bytes[12] >> 4) - 10 : '0' + ((x).bytes[12] >> 4), \
-                ((x).bytes[12] & 15) >= 10 ? 'a' + ((x).bytes[12] & 15) - 10 : '0' + ((x).bytes[12] & 15), \
-                ((x).bytes[13] >> 4) >= 10 ? 'a' + ((x).bytes[13] >> 4) - 10 : '0' + ((x).bytes[13] >> 4), \
-                ((x).bytes[13] & 15) >= 10 ? 'a' + ((x).bytes[13] & 15) - 10 : '0' + ((x).bytes[13] & 15), \
-                ((x).bytes[14] >> 4) >= 10 ? 'a' + ((x).bytes[14] >> 4) - 10 : '0' + ((x).bytes[14] >> 4), \
-                ((x).bytes[14] & 15) >= 10 ? 'a' + ((x).bytes[14] & 15) - 10 : '0' + ((x).bytes[14] & 15), \
-                ((x).bytes[15] >> 4) >= 10 ? 'a' + ((x).bytes[15] >> 4) - 10 : '0' + ((x).bytes[15] >> 4), \
-                ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \
-                0 })
-
-#define SD_ID128_MAKE_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
-        #a #b #c #d #e #f #g #h #i #j #k #l #m #n #o #p
-
-_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
-        return memcmp(&a, &b, 16) == 0;
-}
-
-_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
-        return a.qwords[0] == 0 && a.qwords[1] == 0;
-}
-
-_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
-        return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
-}
-
-#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
-#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-ipv4acd.h b/src/core/systemd/src/systemd/sd-ipv4acd.h
deleted file mode 100644
index 2809d874..00000000
--- a/src/core/systemd/src/systemd/sd-ipv4acd.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdipv4acdfoo
-#define foosdipv4acdfoo
-
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <stdbool.h>
-
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-enum {
-        SD_IPV4ACD_EVENT_STOP           = 0,
-        SD_IPV4ACD_EVENT_BIND           = 1,
-        SD_IPV4ACD_EVENT_CONFLICT       = 2,
-};
-
-typedef struct sd_ipv4acd sd_ipv4acd;
-typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata);
-
-int sd_ipv4acd_detach_event(sd_ipv4acd *acd);
-int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority);
-int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address);
-int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata);
-int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
-int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
-int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd);
-const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd);
-int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address);
-int sd_ipv4acd_is_running(sd_ipv4acd *acd);
-int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts);
-int sd_ipv4acd_stop(sd_ipv4acd *acd);
-sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd);
-sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd);
-int sd_ipv4acd_new(sd_ipv4acd **ret);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-ipv4ll.h b/src/core/systemd/src/systemd/sd-ipv4ll.h
deleted file mode 100644
index aa4d174e..00000000
--- a/src/core/systemd/src/systemd/sd-ipv4ll.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdipv4llfoo
-#define foosdipv4llfoo
-
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/ethernet.h>
-#include <netinet/in.h>
-
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-enum {
-        SD_IPV4LL_EVENT_STOP            = 0,
-        SD_IPV4LL_EVENT_BIND            = 1,
-        SD_IPV4LL_EVENT_CONFLICT        = 2,
-};
-
-typedef struct sd_ipv4ll sd_ipv4ll;
-typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata);
-
-int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
-int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
-int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
-int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
-int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
-int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
-int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll);
-const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll);
-int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed);
-int sd_ipv4ll_is_running(sd_ipv4ll *ll);
-int sd_ipv4ll_restart(sd_ipv4ll *ll);
-int sd_ipv4ll_start(sd_ipv4ll *ll);
-int sd_ipv4ll_stop(sd_ipv4ll *ll);
-sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
-sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
-int sd_ipv4ll_new(sd_ipv4ll **ret);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-lldp.h b/src/core/systemd/src/systemd/sd-lldp.h
deleted file mode 100644
index f551f6b4..00000000
--- a/src/core/systemd/src/systemd/sd-lldp.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdlldphfoo
-#define foosdlldphfoo
-
-/***
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <net/ethernet.h>
-#include <sys/types.h>
-
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-/* IEEE 802.1AB-2009 Clause 8: TLV Types */
-enum {
-        SD_LLDP_TYPE_END                 = 0,
-        SD_LLDP_TYPE_CHASSIS_ID          = 1,
-        SD_LLDP_TYPE_PORT_ID             = 2,
-        SD_LLDP_TYPE_TTL                 = 3,
-        SD_LLDP_TYPE_PORT_DESCRIPTION    = 4,
-        SD_LLDP_TYPE_SYSTEM_NAME         = 5,
-        SD_LLDP_TYPE_SYSTEM_DESCRIPTION  = 6,
-        SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
-        SD_LLDP_TYPE_MGMT_ADDRESS        = 8,
-        SD_LLDP_TYPE_PRIVATE             = 127,
-};
-
-/* IEEE 802.1AB-2009 Clause 8.5.2: Chassis subtypes */
-enum {
-        SD_LLDP_CHASSIS_SUBTYPE_RESERVED            = 0,
-        SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT   = 1,
-        SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS     = 2,
-        SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT      = 3,
-        SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS         = 4,
-        SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS     = 5,
-        SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME      = 6,
-        SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED    = 7,
-};
-
-/* IEEE 802.1AB-2009 Clause 8.5.3: Port subtype */
-enum {
-        SD_LLDP_PORT_SUBTYPE_RESERVED         = 0,
-        SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS  = 1,
-        SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT   = 2,
-        SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS      = 3,
-        SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS  = 4,
-        SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME   = 5,
-        SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
-        SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
-};
-
-/* IEEE 802.1AB-2009 Clause 8.5.8: System capabilities */
-enum {
-        SD_LLDP_SYSTEM_CAPABILITIES_OTHER    = 1 << 0,
-        SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
-        SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE   = 1 << 2,
-        SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP  = 1 << 3,
-        SD_LLDP_SYSTEM_CAPABILITIES_ROUTER   = 1 << 4,
-        SD_LLDP_SYSTEM_CAPABILITIES_PHONE    = 1 << 5,
-        SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS   = 1 << 6,
-        SD_LLDP_SYSTEM_CAPABILITIES_STATION  = 1 << 7,
-        SD_LLDP_SYSTEM_CAPABILITIES_CVLAN    = 1 << 8,
-        SD_LLDP_SYSTEM_CAPABILITIES_SVLAN    = 1 << 9,
-        SD_LLDP_SYSTEM_CAPABILITIES_TPMR     = 1 << 10,
-};
-
-#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1)
-
-#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS                         \
-        ((uint16_t)                                                     \
-         (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER|                         \
-          SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|                           \
-          SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP|                          \
-          SD_LLDP_SYSTEM_CAPABILITIES_ROUTER|                           \
-          SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS|                           \
-          SD_LLDP_SYSTEM_CAPABILITIES_CVLAN|                            \
-          SD_LLDP_SYSTEM_CAPABILITIES_SVLAN|                            \
-          SD_LLDP_SYSTEM_CAPABILITIES_TPMR))
-
-#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
-#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
-
-#define SD_LLDP_OUI_MUD   (uint8_t[]) { 0x00, 0x00, 0x5E }
-#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION  0x01
-
-/* IEEE 802.1AB-2009 Annex E */
-enum {
-        SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID          = 1,
-        SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
-        SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME             = 3,
-        SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY     = 4,
-        SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST      = 5,
-        SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID        = 6,
-        SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION      = 7,
-};
-
-/* IEEE 802.1AB-2009 Annex F */
-enum {
-        SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS = 1,
-        SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI         = 2,
-        SD_LLDP_OUI_802_3_SUBTYPE_LINK_AGGREGATION      = 3,
-        SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE    = 4,
-};
-
-typedef struct sd_lldp sd_lldp;
-typedef struct sd_lldp_neighbor sd_lldp_neighbor;
-
-typedef enum sd_lldp_event {
-        SD_LLDP_EVENT_ADDED,
-        SD_LLDP_EVENT_REMOVED,
-        SD_LLDP_EVENT_UPDATED,
-        SD_LLDP_EVENT_REFRESHED,
-        _SD_LLDP_EVENT_MAX,
-        _SD_LLDP_EVENT_INVALID = -1,
-} sd_lldp_event;
-
-typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata);
-
-int sd_lldp_new(sd_lldp **ret);
-sd_lldp* sd_lldp_ref(sd_lldp *lldp);
-sd_lldp* sd_lldp_unref(sd_lldp *lldp);
-
-int sd_lldp_start(sd_lldp *lldp);
-int sd_lldp_stop(sd_lldp *lldp);
-
-int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority);
-int sd_lldp_detach_event(sd_lldp *lldp);
-sd_event *sd_lldp_get_event(sd_lldp *lldp);
-
-int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata);
-int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex);
-
-/* Controls how much and what to store in the neighbors database */
-int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n);
-int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask);
-int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address);
-
-int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors);
-
-int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
-sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
-sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
-
-/* Access to LLDP frame metadata */
-int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
-int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
-int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
-int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
-
-/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
-int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
-int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret);
-int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
-int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret);
-int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
-int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
-int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
-int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
-int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
-int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
-int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
-
-/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
- * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
-int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
-int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
-int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type);
-int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type);
-int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype);
-int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype);
-int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/systemd/src/systemd/sd-ndisc.h b/src/core/systemd/src/systemd/sd-ndisc.h
deleted file mode 100644
index c0e37899..00000000
--- a/src/core/systemd/src/systemd/sd-ndisc.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdndiscfoo
-#define foosdndiscfoo
-
-/***
-  Copyright © 2014 Intel Corporation. All rights reserved.
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <sys/types.h>
-
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-/* Neighbor Discovery Options, RFC 4861, Section 4.6 and
- * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
-enum {
-        SD_NDISC_OPTION_SOURCE_LL_ADDRESS  = 1,
-        SD_NDISC_OPTION_TARGET_LL_ADDRESS  = 2,
-        SD_NDISC_OPTION_PREFIX_INFORMATION = 3,
-        SD_NDISC_OPTION_MTU                = 5,
-        SD_NDISC_OPTION_ROUTE_INFORMATION  = 24,
-        SD_NDISC_OPTION_RDNSS              = 25,
-        SD_NDISC_OPTION_FLAGS_EXTENSION    = 26,
-        SD_NDISC_OPTION_DNSSL              = 31,
-        SD_NDISC_OPTION_CAPTIVE_PORTAL     = 37,
-};
-
-/* Route preference, RFC 4191, Section 2.1 */
-enum {
-        SD_NDISC_PREFERENCE_LOW    = 3U,
-        SD_NDISC_PREFERENCE_MEDIUM = 0U,
-        SD_NDISC_PREFERENCE_HIGH   = 1U,
-};
-
-typedef struct sd_ndisc sd_ndisc;
-typedef struct sd_ndisc_router sd_ndisc_router;
-
-typedef enum sd_ndisc_event {
-        SD_NDISC_EVENT_TIMEOUT,
-        SD_NDISC_EVENT_ROUTER,
-        _SD_NDISC_EVENT_MAX,
-        _SD_NDISC_EVENT_INVALID = -1,
-} sd_ndisc_event;
-
-typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata);
-
-int sd_ndisc_new(sd_ndisc **ret);
-sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
-sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
-
-int sd_ndisc_start(sd_ndisc *nd);
-int sd_ndisc_stop(sd_ndisc *nd);
-
-int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
-int sd_ndisc_detach_event(sd_ndisc *nd);
-sd_event *sd_ndisc_get_event(sd_ndisc *nd);
-
-int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata);
-int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
-int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
-
-int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret);
-int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret);
-
-int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size);
-sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
-sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
-
-int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
-int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
-int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
-
-int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
-int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags);
-int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret);
-int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime);
-int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret);
-
-/* Generic option access */
-int sd_ndisc_router_option_rewind(sd_ndisc_router *rt);
-int sd_ndisc_router_option_next(sd_ndisc_router *rt);
-int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret);
-int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type);
-int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
-
-/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
-int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret);
-int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret);
-int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret);
-int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
-int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
-
-/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */
-int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
-int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
-int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
-int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret);
-
-/* Specific option access: SD_NDISC_OPTION_RDNSS */
-int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
-int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
-
-/* Specific option access: SD_NDISC_OPTION_DNSSL */
-int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret);
-int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/core/tests/config/test-config.c b/src/core/tests/config/test-config.c
index 3bb14815..6794fbf6 100644
--- a/src/core/tests/config/test-config.c
+++ b/src/core/tests/config/test-config.c
@@ -151,6 +151,7 @@ test_config_simple(void)
     gs_unref_object NMConfig *config  = NULL;
     gs_strfreev char **       plugins = NULL;
     char *                    value;
+    const char *              cvalue;
     gs_unref_object NMDevice *dev50 = nm_test_device_new("00:00:00:00:00:50");
     gs_unref_object NMDevice *dev51 = nm_test_device_new("00:00:00:00:00:51");
     gs_unref_object NMDevice *dev52 = nm_test_device_new("00:00:00:00:00:52");
@@ -206,59 +207,50 @@ test_config_simple(void)
     g_assert_cmpstr(value, ==, "51");
     g_free(value);
 
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ipv6.route-metric",
-                                                  NULL);
-    g_assert_cmpstr(value, ==, NULL);
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ipv4.route-metric",
-                                                  NULL);
-    g_assert_cmpstr(value, ==, "50");
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ipv4.route-metric",
-                                                  dev50);
-    g_assert_cmpstr(value, ==, "50");
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ipv4.route-metric",
-                                                  dev51);
-    g_assert_cmpstr(value, ==, "51");
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ipv4.route-metric",
-                                                  dev52);
-    g_assert_cmpstr(value, ==, "52");
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ethernet.mtu",
-                                                  dev51);
-    g_assert_cmpstr(value, ==, "9000");
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ethernet.mtu",
-                                                  dev50);
-    g_assert_cmpstr(value, ==, "1400");
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ipv4.dns-priority",
-                                                  dev51);
-    g_assert_cmpstr(value, ==, NULL);
-    g_free(value);
-
-    value = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
-                                                  "ipv4.dns-priority",
-                                                  dev50);
-    g_assert_cmpstr(value, ==, "60");
-    g_free(value);
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ipv6.route-metric",
+                                                   NULL);
+    g_assert_cmpstr(cvalue, ==, NULL);
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ipv4.route-metric",
+                                                   NULL);
+    g_assert_cmpstr(cvalue, ==, "50");
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ipv4.route-metric",
+                                                   dev50);
+    g_assert_cmpstr(cvalue, ==, "50");
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ipv4.route-metric",
+                                                   dev51);
+    g_assert_cmpstr(cvalue, ==, "51");
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ipv4.route-metric",
+                                                   dev52);
+    g_assert_cmpstr(cvalue, ==, "52");
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ethernet.mtu",
+                                                   dev51);
+    g_assert_cmpstr(cvalue, ==, "9000");
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ethernet.mtu",
+                                                   dev50);
+    g_assert_cmpstr(cvalue, ==, "1400");
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ipv4.dns-priority",
+                                                   dev51);
+    g_assert_cmpstr(cvalue, ==, NULL);
+
+    cvalue = nm_config_data_get_connection_default(nm_config_get_data_orig(config),
+                                                   "ipv4.dns-priority",
+                                                   dev50);
+    g_assert_cmpstr(cvalue, ==, "60");
 }
 
 static void
@@ -616,7 +608,7 @@ test_config_confdir(void)
 #define ASSERT_GET_CONN_DEFAULT(xconfig, xname, xvalue)                             \
     G_STMT_START                                                                    \
     {                                                                               \
-        gs_free char *_value =                                                      \
+        const char *_value =                                                        \
             nm_config_data_get_connection_default(nm_config_get_data_orig(xconfig), \
                                                   (xname),                          \
                                                   NULL);                            \
diff --git a/src/core/tests/meson.build b/src/core/tests/meson.build
index 994563c4..316f20cc 100644
--- a/src/core/tests/meson.build
+++ b/src/core/tests/meson.build
@@ -37,11 +37,15 @@ exe = executable(
     src_inc,
   ],
   dependencies: [
-    libnm_systemd_core_dep,
-    libnm_systemd_shared_dep,
+    libnm_systemd_shared_dep_inc,
+    libnm_systemd_core_dep_inc,
+    glib_dep,
   ],
-  c_args: [
-    '-DG_LOG_DOMAIN="test"',
+  link_with: [
+    libnm_systemd_shared,
+    libnm_systemd_core,
+    libnm_log_null,
+    libc_siphash,
   ],
 )
 
diff --git a/src/core/tests/test-core-with-expect.c b/src/core/tests/test-core-with-expect.c
index 022cf8f3..01784863 100644
--- a/src/core/tests/test-core-with-expect.c
+++ b/src/core/tests/test-core-with-expect.c
@@ -11,6 +11,7 @@
 #include <fcntl.h>
 
 #include "NetworkManagerUtils.h"
+#include "libnm-platform/nm-platform-utils.h"
 
 #include "nm-test-utils-core.h"
 
@@ -586,7 +587,7 @@ test_nm_ethernet_address_is_valid(void)
 /*****************************************************************************/
 
 static void
-test_nm_utils_new_vlan_name(void)
+test_nmp_utils_new_vlan_name(void)
 {
     guint       i, j;
     const char *parent_names[] = {
@@ -614,7 +615,7 @@ test_nm_utils_new_vlan_name(void)
 
             vlan_id_s = g_strdup_printf(".%d", vlan_id);
 
-            ifname = nm_utils_new_vlan_name(parent_names[i], vlan_id);
+            ifname = nmp_utils_new_vlan_name(parent_names[i], vlan_id);
             g_assert(ifname && ifname[0]);
             g_assert_cmpint(strlen(ifname),
                             ==,
@@ -643,7 +644,7 @@ main(int argc, char **argv)
     g_test_add_func("/general/nm_utils_array_remove_at_indexes",
                     test_nm_utils_array_remove_at_indexes);
     g_test_add_func("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid);
-    g_test_add_func("/general/nm_utils_new_vlan_name", test_nm_utils_new_vlan_name);
+    g_test_add_func("/general/nmp_utils_new_vlan_name", test_nmp_utils_new_vlan_name);
 
     return g_test_run();
 }
diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c
index 8698c406..f28c4178 100644
--- a/src/core/tests/test-core.c
+++ b/src/core/tests/test-core.c
@@ -11,16 +11,36 @@
 /* need math.h for isinf() and INFINITY. No need to link with -lm */
 #include <math.h>
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-core-utils.h"
-#include "systemd/nm-sd-utils-core.h"
+#include "libnm-systemd-core/nm-sd-utils-core.h"
 
 #include "dns/nm-dns-manager.h"
 #include "nm-connectivity.h"
 
 #include "nm-test-utils-core.h"
 
+/*****************************************************************************/
+
+static void
+test_config_h(void)
+{
+#define ABSOLUTE_PATH(path)                  \
+    G_STMT_START                             \
+    {                                        \
+        g_assert_cmpstr("" path "", !=, ""); \
+        g_assert("" path ""[0] == '/');      \
+    }                                        \
+    G_STMT_END
+
+    ABSOLUTE_PATH(IPTABLES_PATH);
+    ABSOLUTE_PATH(NFT_PATH);
+}
+
+/*****************************************************************************/
+
 /* Reference implementation for nm_utils_ip6_address_clear_host_address.
  * Taken originally from set_address_masked(), src/ndisc/nm-lndp-ndisc.c
  **/
@@ -867,7 +887,7 @@ test_connection_no_match_vlan(void)
     /* Check that the connections do not match if VLAN flags differ */
     s_vlan_orig = nm_connection_get_setting_vlan(orig);
     g_assert(s_vlan_orig);
-    g_object_set(G_OBJECT(s_vlan_orig), NM_SETTING_VLAN_FLAGS, NM_VLAN_FLAG_REORDER_HEADERS, NULL);
+    g_object_set(G_OBJECT(s_vlan_orig), NM_SETTING_VLAN_FLAGS, _NM_VLAN_FLAG_REORDER_HEADERS, NULL);
 
     s_vlan_copy = nm_connection_get_setting_vlan(copy);
     g_assert(s_vlan_copy);
@@ -2231,7 +2251,7 @@ test_utils_file_is_in_path(void)
     g_assert(!nm_utils_file_is_in_path("//b///a/", "/b//"));
     g_assert(!nm_utils_file_is_in_path("//b///a/", "/b/a/"));
     g_assert(!nm_utils_file_is_in_path("//b///a", "/b/a/"));
-    g_assert(nm_utils_file_is_in_path("//b///a/.", "/b/a/"));
+    g_assert(!nm_utils_file_is_in_path("//b///a/.", "/b/a/"));
     g_assert(nm_utils_file_is_in_path("//b///a/..", "/b/a/"));
 }
 
@@ -2569,6 +2589,8 @@ main(int argc, char **argv)
 {
     nmtst_init_with_logging(&argc, &argv, NULL, "ALL");
 
+    g_test_add_func("/general/test_config_h", test_config_h);
+
     g_test_add_func("/general/test_logging_domains", test_logging_domains);
     g_test_add_func("/general/test_logging_error", test_logging_error);
 
diff --git a/src/core/tests/test-ip4-config.c b/src/core/tests/test-ip4-config.c
index 27b1d611..3a095d4a 100644
--- a/src/core/tests/test-ip4-config.c
+++ b/src/core/tests/test-ip4-config.c
@@ -8,7 +8,7 @@
 #include <arpa/inet.h>
 
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #include "nm-test-utils-core.h"
 
diff --git a/src/core/tests/test-ip6-config.c b/src/core/tests/test-ip6-config.c
index 3b48fb7f..ddf4c789 100644
--- a/src/core/tests/test-ip6-config.c
+++ b/src/core/tests/test-ip6-config.c
@@ -10,7 +10,7 @@
 
 #include "nm-ip6-config.h"
 
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-test-utils-core.h"
 
 static NMIP6Config *
diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c
index 43132c6d..ba065c09 100644
--- a/src/core/tests/test-l3cfg.c
+++ b/src/core/tests/test-l3cfg.c
@@ -5,7 +5,7 @@
 #include "nm-l3cfg.h"
 #include "nm-l3-ipv4ll.h"
 #include "nm-netns.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 
 #include "platform/tests/test-common.h"
 
@@ -82,8 +82,8 @@ _test_fixture_1_setup(TestFixture1 *f, int test_idx)
     f->hwaddr0 = l0->l_address;
     f->hwaddr1 = l1->l_address;
 
-    g_assert(nm_platform_link_set_up(f->platform, f->ifindex0, NULL));
-    g_assert(nm_platform_link_set_up(f->platform, f->ifindex1, NULL));
+    g_assert(nm_platform_link_change_flags(f->platform, f->ifindex0, IFF_UP, TRUE) >= 0);
+    g_assert(nm_platform_link_change_flags(f->platform, f->ifindex1, IFF_UP, TRUE) >= 0);
 
     return f;
 }
@@ -187,6 +187,12 @@ _test_l3cfg_signal_notify(NML3Cfg *                   l3cfg,
             nm_assert(NM_IS_L3_CONFIG_DATA(ti->l3cd));
             nm_assert(ti->tag);
         }
+    } else if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED) {
+        g_assert(!notify_data->l3cd_changed.l3cd_old
+                 || NM_IS_L3_CONFIG_DATA(notify_data->l3cd_changed.l3cd_old));
+        g_assert(!notify_data->l3cd_changed.l3cd_new
+                 || NM_IS_L3_CONFIG_DATA(notify_data->l3cd_changed.l3cd_new));
+        return;
     }
 
     switch (tdata->notify_type) {
diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c
index 03576c13..12d2b911 100644
--- a/src/core/tests/test-systemd.c
+++ b/src/core/tests/test-systemd.c
@@ -3,68 +3,12 @@
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
-#include "src/core/systemd/nm-default-systemd.h"
+#include "libnm-systemd-core/nm-default-systemd-core.h"
 
-#include "systemd/nm-sd.h"
-#include "systemd/nm-sd-utils-shared.h"
+#include "libnm-systemd-core/nm-sd.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
-#include "nm-test-utils-core.h"
-
-/*****************************************************************************
- * Stub implementations of libNetworkManagerBase symbols
- *****************************************************************************/
-
-gboolean
-nm_utils_get_testing_initialized(void)
-{
-    return TRUE;
-}
-
-void
-_nm_utils_set_testing(NMUtilsTestFlags flags)
-{
-    g_assert_not_reached();
-}
-
-gint32
-nm_utils_get_monotonic_timestamp_sec(void)
-{
-    return 1;
-}
-
-NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL];
-
-gboolean
-_nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain)
-{
-    return FALSE;
-}
-
-void
-_nm_log_impl(const char *file,
-             guint       line,
-             const char *func,
-             gboolean    mt_require_locking,
-             NMLogLevel  level,
-             NMLogDomain domain,
-             int         error,
-             const char *ifname,
-             const char *con_uuid,
-             const char *fmt,
-             ...)
-{}
-
-gboolean
-nm_logging_setup(const char *level, const char *domains, char **bad_domains, GError **error)
-{
-    return TRUE;
-}
-
-const char *
-nm_strerror_native(int errsv)
-{
-    return g_strerror(errsv);
-}
+#include "libnm-glib-aux/nm-test-utils.h"
 
 /*****************************************************************************/
 
@@ -178,46 +122,34 @@ test_sd_event(void)
 static void
 test_path_equal(void)
 {
-#define _path_equal_check1(path, kill_dots, expected)                                  \
-    G_STMT_START                                                                       \
-    {                                                                                  \
-        const gboolean _kill_dots = (kill_dots);                                       \
-        const char *   _path0     = (path);                                            \
-        const char *   _expected  = (expected);                                        \
-        gs_free char * _path      = g_strdup(_path0);                                  \
-        const char *   _path_result;                                                   \
-                                                                                       \
-        if (!_kill_dots && !nm_sd_utils_path_equal(_path0, _expected))                 \
-            g_error("Paths \"%s\" and \"%s\" don't compare equal", _path0, _expected); \
-                                                                                       \
-        _path_result = nm_sd_utils_path_simplify(_path, _kill_dots);                   \
-        g_assert(_path_result == _path);                                               \
-        g_assert_cmpstr(_path, ==, _expected);                                         \
-    }                                                                                  \
-    G_STMT_END
-
-#define _path_equal_check(path, expected_no_kill_dots, expected_kill_dots)           \
-    G_STMT_START                                                                     \
-    {                                                                                \
-        _path_equal_check1(path, FALSE, expected_no_kill_dots);                      \
-        _path_equal_check1(path, TRUE, expected_kill_dots ?: expected_no_kill_dots); \
-    }                                                                                \
+#define _path_equal_check(path, expected)                \
+    G_STMT_START                                         \
+    {                                                    \
+        const char *  _path0    = (path);                \
+        const char *  _expected = (expected);            \
+        gs_free char *_path     = g_strdup(_path0);      \
+        const char *  _path_result;                      \
+                                                         \
+        _path_result = nm_sd_utils_path_simplify(_path); \
+        g_assert(_path_result == _path);                 \
+        g_assert_cmpstr(_path, ==, _expected);           \
+    }                                                    \
     G_STMT_END
 
-    _path_equal_check("", "", NULL);
-    _path_equal_check(".", ".", NULL);
-    _path_equal_check("..", "..", NULL);
-    _path_equal_check("/..", "/..", NULL);
-    _path_equal_check("//..", "/..", NULL);
-    _path_equal_check("/.", "/.", "/");
-    _path_equal_check("./", ".", ".");
-    _path_equal_check("./.", "./.", ".");
-    _path_equal_check(".///.", "./.", ".");
-    _path_equal_check(".///./", "./.", ".");
-    _path_equal_check(".////", ".", ".");
-    _path_equal_check("//..//foo/", "/../foo", NULL);
-    _path_equal_check("///foo//./bar/.", "/foo/./bar/.", "/foo/bar");
-    _path_equal_check(".//./foo//./bar/.", "././foo/./bar/.", "foo/bar");
+    _path_equal_check("", "");
+    _path_equal_check(".", ".");
+    _path_equal_check("..", "..");
+    _path_equal_check("/..", "/..");
+    _path_equal_check("//..", "/..");
+    _path_equal_check("/.", "/");
+    _path_equal_check("./", ".");
+    _path_equal_check("./.", ".");
+    _path_equal_check(".///.", ".");
+    _path_equal_check(".///./", ".");
+    _path_equal_check(".////", ".");
+    _path_equal_check("//..//foo/", "/../foo");
+    _path_equal_check("///foo//./bar/.", "/foo/bar");
+    _path_equal_check(".//./foo//./bar/.", "foo/bar");
 }
 
 /*****************************************************************************/
@@ -247,11 +179,11 @@ _test_unbase64mem_mem(const char *base64, const guint8 *expected_arr, gsize expe
 {
     gs_free char *expected_base64 = NULL;
     int           r;
-    gs_free guint8 *exp2_arr = NULL;
-    gs_free guint8 *exp3_arr = NULL;
-    gsize           exp2_len;
-    gsize           exp3_len;
-    gsize           i;
+    nm_auto_free guint8 *exp2_arr = NULL;
+    nm_auto_free guint8 *exp3_arr = NULL;
+    gsize                exp2_len;
+    gsize                exp3_len;
+    gsize                i;
 
     expected_base64 = g_base64_encode(expected_arr, expected_len);
 
@@ -327,7 +259,7 @@ NMTST_DEFINE();
 int
 main(int argc, char **argv)
 {
-    nmtst_init_assert_logging(&argc, &argv, "INFO", "ALL");
+    nmtst_init(&argc, &argv, TRUE);
 
     g_test_add_func("/systemd/dhcp/create", test_dhcp_create);
     g_test_add_func("/systemd/lldp/create", test_lldp_create);
diff --git a/src/core/vpn/nm-vpn-connection.c b/src/core/vpn/nm-vpn-connection.c
index 02831b08..d06ca46a 100644
--- a/src/core/vpn/nm-vpn-connection.c
+++ b/src/core/vpn/nm-vpn-connection.c
@@ -14,21 +14,22 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <linux/if.h>
 #include <linux/rtnetlink.h>
 
 #include "nm-proxy-config.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-active-connection.h"
 #include "NetworkManagerUtils.h"
 #include "settings/nm-settings-connection.h"
 #include "nm-dispatcher.h"
 #include "nm-netns.h"
 #include "settings/nm-agent-manager.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-pacrunner-manager.h"
-#include "nm-firewall-manager.h"
+#include "nm-firewalld-manager.h"
 #include "nm-config.h"
 #include "nm-vpn-plugin-info.h"
 #include "nm-vpn-manager.h"
@@ -95,7 +96,7 @@ typedef struct {
     NMVpnPluginInfo * plugin_info;
     char *            bus_name;
 
-    NMFirewallManagerCallId *fw_call;
+    NMFirewalldManagerCallId *fw_call;
 
     NMNetns *netns;
 
@@ -341,7 +342,7 @@ fw_call_cleanup(NMVpnConnection *self)
     NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self);
 
     if (priv->fw_call) {
-        nm_firewall_manager_cancel_call(priv->fw_call);
+        nm_firewalld_manager_cancel_call(priv->fw_call);
         g_warn_if_fail(!priv->fw_call);
         priv->fw_call = NULL;
     }
@@ -371,7 +372,7 @@ vpn_cleanup(NMVpnConnection *self, NMDevice *parent_dev)
     if (priv->ip_ifindex) {
         NMPlatform *platform = nm_netns_get_platform(priv->netns);
 
-        nm_platform_link_set_down(platform, priv->ip_ifindex);
+        nm_platform_link_change_flags(platform, priv->ip_ifindex, IFF_UP, FALSE);
         nm_platform_ip_route_flush(platform, AF_UNSPEC, priv->ip_ifindex);
         nm_platform_ip_address_flush(platform, AF_UNSPEC, priv->ip_ifindex);
     }
@@ -380,11 +381,11 @@ vpn_cleanup(NMVpnConnection *self, NMDevice *parent_dev)
 
     /* Remove zone from firewall */
     if (priv->ip_iface) {
-        nm_firewall_manager_remove_from_zone(nm_firewall_manager_get(),
-                                             priv->ip_iface,
-                                             NULL,
-                                             NULL,
-                                             NULL);
+        nm_firewalld_manager_remove_from_zone(nm_firewalld_manager_get(),
+                                              priv->ip_iface,
+                                              NULL,
+                                              NULL,
+                                              NULL);
     }
     /* Cancel pending firewall call */
     fw_call_cleanup(self);
@@ -398,11 +399,6 @@ vpn_cleanup(NMVpnConnection *self, NMDevice *parent_dev)
 
     g_free(priv->bus_name);
     priv->bus_name = NULL;
-
-    /* Clear out connection secrets to ensure that the settings service
-     * gets asked for them next time the connection is activated.
-     */
-    nm_active_connection_clear_secrets(NM_ACTIVE_CONNECTION(self));
 }
 
 static void
@@ -964,11 +960,6 @@ plugin_state_changed(NMVpnConnection *self, NMVpnServiceState new_service_state)
     priv->service_state = new_service_state;
 
     if (new_service_state == NM_VPN_SERVICE_STATE_STOPPED) {
-        /* Clear connection secrets to ensure secrets get requested each time the
-         * connection is activated.
-         */
-        nm_active_connection_clear_secrets(NM_ACTIVE_CONNECTION(self));
-
         if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) {
             VpnState old_state = priv->vpn_state;
 
@@ -1157,7 +1148,10 @@ nm_vpn_connection_apply_config(NMVpnConnection *self)
     apply_parent_device_config(self);
 
     if (priv->ip_ifindex > 0) {
-        nm_platform_link_set_up(nm_netns_get_platform(priv->netns), priv->ip_ifindex, NULL);
+        nm_platform_link_change_flags(nm_netns_get_platform(priv->netns),
+                                      priv->ip_ifindex,
+                                      IFF_UP,
+                                      TRUE);
 
         if (priv->ip4_config) {
             nm_assert(priv->ip_ifindex == nm_ip4_config_get_ifindex(priv->ip4_config));
@@ -1211,10 +1205,10 @@ _cleanup_failed_config(NMVpnConnection *self)
 }
 
 static void
-fw_change_zone_cb(NMFirewallManager *      firewall_manager,
-                  NMFirewallManagerCallId *call_id,
-                  GError *                 error,
-                  gpointer                 user_data)
+fw_change_zone_cb(NMFirewalldManager *      firewalld_manager,
+                  NMFirewalldManagerCallId *call_id,
+                  GError *                  error,
+                  gpointer                  user_data)
 {
     NMVpnConnection *       self = user_data;
     NMVpnConnectionPrivate *priv;
@@ -1270,12 +1264,12 @@ nm_vpn_connection_config_maybe_complete(NMVpnConnection *self, gboolean success)
                   NM_PRINT_FMT_QUOTED(zone, "'", zone, "'", "(default)"),
                   priv->ip_iface);
             fw_call_cleanup(self);
-            priv->fw_call = nm_firewall_manager_add_or_change_zone(nm_firewall_manager_get(),
-                                                                   priv->ip_iface,
-                                                                   zone,
-                                                                   FALSE,
-                                                                   fw_change_zone_cb,
-                                                                   self);
+            priv->fw_call = nm_firewalld_manager_add_or_change_zone(nm_firewalld_manager_get(),
+                                                                    priv->ip_iface,
+                                                                    zone,
+                                                                    FALSE,
+                                                                    fw_change_zone_cb,
+                                                                    self);
             return;
         } else if (nm_vpn_connection_apply_config(self))
             return;
@@ -1632,7 +1626,7 @@ nm_vpn_connection_ip4_config_get(NMVpnConnection *self, GVariant *dict)
                 route.metric        = route_metric;
                 route.rt_source     = NM_IP_CONFIG_SOURCE_VPN;
 
-                if (plen > 32 || plen == 0)
+                if (plen > 32)
                     break;
                 route.plen    = plen;
                 route.network = nm_utils_ip4_address_clear_host_address(route.network, plen);
@@ -1825,7 +1819,7 @@ nm_vpn_connection_ip6_config_get(NMVpnConnection *self, GVariant *dict)
             if (!ip6_addr_from_variant(dest, &route.network))
                 goto next;
 
-            if (prefix > 128 || prefix == 0)
+            if (prefix > 128)
                 goto next;
 
             route.plen = prefix;
@@ -2912,16 +2906,14 @@ static const GDBusSignalInfo signal_info_vpn_state_changed = NM_DEFINE_GDBUS_SIG
 static const NMDBusInterfaceInfoExtended interface_info_vpn_connection = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_VPN_CONNECTION,
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_vpn_state_changed, ),
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_vpn_state_changed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("VpnState",
-                                                             "u",
-                                                             NM_VPN_CONNECTION_VPN_STATE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Banner",
-                                                             "s",
-                                                             NM_VPN_CONNECTION_BANNER), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("VpnState",
+                                                           "u",
+                                                           NM_VPN_CONNECTION_VPN_STATE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Banner",
+                                                           "s",
+                                                           NM_VPN_CONNECTION_BANNER), ), ),
 };
 
 static void
diff --git a/src/core/vpn/nm-vpn-connection.h b/src/core/vpn/nm-vpn-connection.h
index 66ec7eec..0209ea3d 100644
--- a/src/core/vpn/nm-vpn-connection.h
+++ b/src/core/vpn/nm-vpn-connection.h
@@ -9,7 +9,7 @@
 
 #include "nm-vpn-dbus-interface.h"
 #include "devices/nm-device.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-active-connection.h"
 #include "nm-vpn-plugin-info.h"
 
diff --git a/src/core/vpn/nm-vpn-manager.c b/src/core/vpn/nm-vpn-manager.c
index 53fe58dd..f30dba79 100644
--- a/src/core/vpn/nm-vpn-manager.c
+++ b/src/core/vpn/nm-vpn-manager.c
@@ -12,7 +12,7 @@
 #include "nm-vpn-connection.h"
 #include "nm-setting-vpn.h"
 #include "nm-vpn-dbus-interface.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 typedef struct {
     GSList *      plugins;